OPENDJ-1607 Revert changes to revision 11339
3 files deleted
4 files added
44 files modified
| New file |
| | |
| | | /* |
| | | * 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 2009-2010 Sun Microsystems, Inc. |
| | | * Portions Copyright 2011-2014 ForgeRock AS. |
| | | */ |
| | | package com.forgerock.opendj.cli; |
| | | |
| | | import java.util.concurrent.atomic.AtomicReference; |
| | | |
| | | import org.forgerock.opendj.ldap.AbstractConnectionWrapper; |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.ConnectionFactory; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.LdapPromise; |
| | | import org.forgerock.opendj.ldap.IntermediateResponseHandler; |
| | | import org.forgerock.opendj.ldap.requests.BindRequest; |
| | | import org.forgerock.opendj.ldap.responses.BindResult; |
| | | import org.forgerock.util.Reject; |
| | | import org.forgerock.util.promise.AsyncFunction; |
| | | import org.forgerock.util.promise.FailureHandler; |
| | | import org.forgerock.util.promise.Function; |
| | | import org.forgerock.util.promise.Promise; |
| | | import org.forgerock.util.promise.SuccessHandler; |
| | | |
| | | import static org.forgerock.util.Utils.*; |
| | | /** |
| | | * An authenticated connection factory can be used to create pre-authenticated |
| | | * connections to a Directory Server. |
| | | * <p> |
| | | * The connections returned by an authenticated connection factory support all |
| | | * operations with the exception of Bind requests. Attempts to perform a Bind |
| | | * will result in an {@code UnsupportedOperationException}. |
| | | * <p> |
| | | * In addition, the returned connections support retrieval of the |
| | | * {@code BindResult} returned from the initial Bind request, or last rebind. |
| | | * <p> |
| | | * Support for connection re-authentication is provided through the |
| | | * {@link #setRebindAllowed} method which, if set to {@code true}, causes |
| | | * subsequent connections created using the factory to support the |
| | | * {@code rebind} method. |
| | | * <p> |
| | | * If the Bind request fails for some reason (e.g. invalid credentials), then |
| | | * the connection attempt will fail and an {@link LdapException} will be thrown. |
| | | */ |
| | | public final class AuthenticatedConnectionFactory implements ConnectionFactory { |
| | | |
| | | /** |
| | | * An authenticated connection supports all operations except Bind |
| | | * operations. |
| | | */ |
| | | public static final class AuthenticatedConnection extends AbstractConnectionWrapper<Connection> { |
| | | |
| | | private final BindRequest request; |
| | | private volatile BindResult result; |
| | | |
| | | private AuthenticatedConnection(final Connection connection, final BindRequest request, |
| | | final BindResult result) { |
| | | super(connection); |
| | | this.request = request; |
| | | this.result = result; |
| | | } |
| | | |
| | | /* |
| | | * Bind operations are not supported by pre-authenticated connections. |
| | | * These methods will always throw {@code UnsupportedOperationException}. |
| | | */ |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public BindResult bind(BindRequest request) throws LdapException { |
| | | throw new UnsupportedOperationException(); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public BindResult bind(String name, char[] password) throws LdapException { |
| | | throw new UnsupportedOperationException(); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public LdapPromise<BindResult> bindAsync(BindRequest request, |
| | | IntermediateResponseHandler intermediateResponseHandler) { |
| | | throw new UnsupportedOperationException(); |
| | | } |
| | | |
| | | /** |
| | | * Returns an unmodifiable view of the Bind result which was returned |
| | | * from the server after authentication. |
| | | * |
| | | * @return The Bind result which was returned from the server after |
| | | * authentication. |
| | | */ |
| | | public BindResult getAuthenticatedBindResult() { |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * Re-authenticates to the Directory Server using the bind request |
| | | * associated with this connection. If re-authentication fails for some |
| | | * reason then this connection will be automatically closed. |
| | | * |
| | | * @return A promise representing the result of the operation. |
| | | * @throws UnsupportedOperationException |
| | | * If this connection does not support rebind operations. |
| | | * @throws IllegalStateException |
| | | * If this connection has already been closed, i.e. if |
| | | * {@code isClosed() == true}. |
| | | */ |
| | | public LdapPromise<BindResult> rebindAsync() { |
| | | if (request == null) { |
| | | throw new UnsupportedOperationException(); |
| | | } |
| | | |
| | | return (LdapPromise<BindResult>) connection.bindAsync(request) |
| | | .onSuccess(new SuccessHandler<BindResult>() { |
| | | @Override |
| | | public void handleResult(final BindResult result) { |
| | | // Save the result. |
| | | AuthenticatedConnection.this.result = result; |
| | | } |
| | | }).onFailure(new FailureHandler<LdapException>() { |
| | | @Override |
| | | public void handleError(final LdapException error) { |
| | | /* |
| | | * This connection is now unauthenticated so prevent further use. |
| | | */ |
| | | connection.close(); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * Returns the string representation of this authenticated connection. |
| | | * |
| | | * @return The string representation of this authenticated connection factory. |
| | | */ |
| | | @Override |
| | | public String toString() { |
| | | StringBuilder builder = new StringBuilder(); |
| | | builder.append("AuthenticatedConnection("); |
| | | builder.append(connection); |
| | | builder.append(')'); |
| | | return builder.toString(); |
| | | } |
| | | |
| | | } |
| | | |
| | | private final BindRequest request; |
| | | private final ConnectionFactory parentFactory; |
| | | private boolean allowRebinds; |
| | | |
| | | /** |
| | | * Creates a new authenticated connection factory which will obtain |
| | | * connections using the provided connection factory and immediately perform |
| | | * the provided Bind request. |
| | | * |
| | | * @param factory |
| | | * The connection factory to use for connecting to the Directory |
| | | * Server. |
| | | * @param request |
| | | * The Bind request to use for authentication. |
| | | * @throws NullPointerException |
| | | * If {@code factory} or {@code request} was {@code null}. |
| | | */ |
| | | public AuthenticatedConnectionFactory(final ConnectionFactory factory, final BindRequest request) { |
| | | Reject.ifNull(factory, request); |
| | | this.parentFactory = factory; |
| | | |
| | | // FIXME: should do a defensive copy. |
| | | this.request = request; |
| | | } |
| | | |
| | | @Override |
| | | public void close() { |
| | | parentFactory.close(); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public Connection getConnection() throws LdapException { |
| | | final Connection connection = parentFactory.getConnection(); |
| | | BindResult bindResult = null; |
| | | try { |
| | | bindResult = connection.bind(request); |
| | | } finally { |
| | | if (bindResult == null) { |
| | | connection.close(); |
| | | } |
| | | } |
| | | |
| | | /* |
| | | * If the bind didn't succeed then an exception will have been thrown |
| | | * and this line will not be reached. |
| | | */ |
| | | return new AuthenticatedConnection(connection, request, bindResult); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public Promise<Connection, LdapException> getConnectionAsync() { |
| | | final AtomicReference<Connection> connectionHolder = new AtomicReference<Connection>(); |
| | | return parentFactory.getConnectionAsync() |
| | | .thenAsync( |
| | | new AsyncFunction<Connection, BindResult, LdapException>() { |
| | | @Override |
| | | public Promise<BindResult, LdapException> apply(final Connection connection) |
| | | throws LdapException { |
| | | connectionHolder.set(connection); |
| | | return connection.bindAsync(request); |
| | | } |
| | | } |
| | | ).then( |
| | | new Function<BindResult, Connection, LdapException>() { |
| | | @Override |
| | | public Connection apply(BindResult result) throws LdapException { |
| | | // FIXME: should make the result unmodifiable. |
| | | return new AuthenticatedConnection(connectionHolder.get(), request, result); |
| | | } |
| | | }, |
| | | new Function<LdapException, Connection, LdapException>() { |
| | | @Override |
| | | public Connection apply(LdapException errorResult) throws LdapException { |
| | | closeSilently(connectionHolder.get()); |
| | | throw errorResult; |
| | | } |
| | | } |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * Indicates whether or not rebind requests are to be supported by |
| | | * connections created by this authenticated connection factory. |
| | | * <p> |
| | | * Rebind requests are invoked using the connection's {@code rebind} method |
| | | * which will throw an {@code UnsupportedOperationException} if rebinds are |
| | | * not supported (the default). |
| | | * |
| | | * @return allowRebinds {@code true} if the {@code rebind} operation is to |
| | | * be supported, otherwise {@code false}. |
| | | */ |
| | | boolean isRebindAllowed() { |
| | | return allowRebinds; |
| | | } |
| | | |
| | | /** |
| | | * Specifies whether or not rebind requests are to be supported by |
| | | * connections created by this authenticated connection factory. |
| | | * <p> |
| | | * Rebind requests are invoked using the connection's {@code rebind} method |
| | | * which will throw an {@code UnsupportedOperationException} if rebinds are |
| | | * not supported (the default). |
| | | * |
| | | * @param allowRebinds |
| | | * {@code true} if the {@code rebind} operation is to be |
| | | * supported, otherwise {@code false}. |
| | | * @return A reference to this connection factory. |
| | | */ |
| | | AuthenticatedConnectionFactory setRebindAllowed(final boolean allowRebinds) { |
| | | this.allowRebinds = allowRebinds; |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * Returns the string representation of this authenticated connection factory. |
| | | * |
| | | * @return The string representation of this authenticated connection factory. |
| | | */ |
| | | @Override |
| | | public String toString() { |
| | | final StringBuilder builder = new StringBuilder(); |
| | | builder.append("AuthenticatedConnectionFactory("); |
| | | builder.append(parentFactory); |
| | | builder.append(')'); |
| | | return builder.toString(); |
| | | } |
| | | |
| | | } |
| | |
| | | */ |
| | | package com.forgerock.opendj.cli; |
| | | |
| | | import static com.forgerock.opendj.cli.ArgumentConstants.*; |
| | | import static com.forgerock.opendj.cli.CliMessages.*; |
| | | import static com.forgerock.opendj.cli.CliConstants.DEFAULT_LDAP_PORT; |
| | | import static com.forgerock.opendj.cli.Utils.getHostNameForLdapUrl; |
| | | |
| | | import java.io.File; |
| | | import java.io.FileInputStream; |
| | | import java.io.IOException; |
| | |
| | | import org.forgerock.i18n.slf4j.LocalizedLogger; |
| | | import org.forgerock.opendj.ldap.ConnectionFactory; |
| | | import org.forgerock.opendj.ldap.KeyManagers; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.LDAPOptions; |
| | | import org.forgerock.opendj.ldap.SSLContextBuilder; |
| | | import org.forgerock.opendj.ldap.TrustManagers; |
| | |
| | | import org.forgerock.opendj.ldap.requests.PlainSASLBindRequest; |
| | | import org.forgerock.opendj.ldap.requests.Requests; |
| | | |
| | | import static java.util.concurrent.TimeUnit.*; |
| | | |
| | | import static org.forgerock.opendj.ldap.Connections.*; |
| | | |
| | | import static com.forgerock.opendj.cli.ArgumentConstants.*; |
| | | import static com.forgerock.opendj.cli.CliConstants.*; |
| | | import static com.forgerock.opendj.cli.CliMessages.*; |
| | | import static com.forgerock.opendj.cli.Utils.*; |
| | | |
| | | /** |
| | | * A connection factory designed for use with command line tools. |
| | | */ |
| | |
| | | /** The Logger. */ |
| | | static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); |
| | | |
| | | private static final long DEFAULT_TIMEOUT_SECONDS = 3; |
| | | |
| | | /** The 'hostName' global argument. */ |
| | | private final StringArgument hostNameArg; |
| | | private StringArgument hostNameArg; |
| | | |
| | | /** The 'port' global argument. */ |
| | | private final IntegerArgument portArg; |
| | | private IntegerArgument portArg; |
| | | |
| | | /** The 'bindDN' global argument. */ |
| | | private final StringArgument bindNameArg; |
| | | private StringArgument bindNameArg; |
| | | |
| | | /** The 'bindPasswordFile' global argument. */ |
| | | private final FileBasedArgument bindPasswordFileArg; |
| | | private FileBasedArgument bindPasswordFileArg; |
| | | |
| | | /** The 'password' value. */ |
| | | private char[] password; |
| | | |
| | | /** The 'bindPassword' global argument. */ |
| | | private final StringArgument bindPasswordArg; |
| | | private StringArgument bindPasswordArg; |
| | | |
| | | /** The 'connectTimeOut' global argument. */ |
| | | private final IntegerArgument connectTimeOut; |
| | | private IntegerArgument connectTimeOut; |
| | | |
| | | /** The 'trustAllArg' global argument. */ |
| | | private final BooleanArgument trustAllArg; |
| | | private BooleanArgument trustAllArg; |
| | | |
| | | /** The 'trustStore' global argument. */ |
| | | private final StringArgument trustStorePathArg; |
| | | private StringArgument trustStorePathArg; |
| | | |
| | | /** The 'trustStorePassword' global argument. */ |
| | | private final StringArgument trustStorePasswordArg; |
| | | private StringArgument trustStorePasswordArg; |
| | | |
| | | /** The 'trustStorePasswordFile' global argument. */ |
| | | private final FileBasedArgument trustStorePasswordFileArg; |
| | | private FileBasedArgument trustStorePasswordFileArg; |
| | | |
| | | /** The 'keyStore' global argument. */ |
| | | private final StringArgument keyStorePathArg; |
| | | private StringArgument keyStorePathArg; |
| | | |
| | | /** The 'keyStorePassword' global argument. */ |
| | | private final StringArgument keyStorePasswordArg; |
| | | private StringArgument keyStorePasswordArg; |
| | | |
| | | /** The 'keyStorePasswordFile' global argument. */ |
| | | private final FileBasedArgument keyStorePasswordFileArg; |
| | | private FileBasedArgument keyStorePasswordFileArg; |
| | | |
| | | /** The 'certNicknameArg' global argument. */ |
| | | private final StringArgument certNicknameArg; |
| | | private StringArgument certNicknameArg; |
| | | |
| | | /** The 'useSSLArg' global argument. */ |
| | | private final BooleanArgument useSSLArg; |
| | | private BooleanArgument useSSLArg; |
| | | |
| | | /** The 'useStartTLSArg' global argument. */ |
| | | private final BooleanArgument useStartTLSArg; |
| | | private BooleanArgument useStartTLSArg; |
| | | |
| | | /** Argument indicating a SASL option. */ |
| | | private final StringArgument saslOptionArg; |
| | | private StringArgument saslOptionArg; |
| | | |
| | | /** |
| | | * Whether to request that the server return the authorization ID in the |
| | |
| | | } |
| | | } |
| | | } catch (final Exception e) { |
| | | throw new ArgumentException(ERR_LDAP_CONN_CANNOT_INITIALIZE_SSL.get(e.toString()), e); |
| | | throw new ArgumentException(ERR_LDAP_CONN_CANNOT_INITIALIZE_SSL.get(e.toString()), |
| | | e); |
| | | } |
| | | |
| | | LDAPOptions options = new LDAPOptions(); |
| | |
| | | options.setSSLContext(sslContext).setUseStartTLS(useStartTLSArg.isPresent()); |
| | | } |
| | | options.setConnectTimeout(getConnectTimeout(), TimeUnit.MILLISECONDS); |
| | | connFactory = newLDAPConnectionFactory(hostNameArg.getValue(), port, options); |
| | | connFactory = new LDAPConnectionFactory(hostNameArg.getValue(), port, options); |
| | | } |
| | | return connFactory; |
| | | } |
| | |
| | | authenticatedConnFactory = getConnectionFactory(); |
| | | final BindRequest bindRequest = getBindRequest(); |
| | | if (bindRequest != null) { |
| | | app.setBindRequest(bindRequest); |
| | | authenticatedConnFactory = newLDAPConnectionFactory(hostNameArg.getValue(), port, |
| | | new LDAPOptions().setBindRequest(bindRequest).setTimeout(DEFAULT_TIMEOUT_SECONDS, SECONDS)); |
| | | authenticatedConnFactory = new AuthenticatedConnectionFactory(authenticatedConnFactory, bindRequest); |
| | | } |
| | | } |
| | | return authenticatedConnFactory; |
| | |
| | | |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | | import org.forgerock.i18n.slf4j.LocalizedLogger; |
| | | import org.forgerock.opendj.ldap.requests.BindRequest; |
| | | |
| | | /** |
| | | * This class provides an abstract base class which can be used as the basis of a console-based application. |
| | |
| | | |
| | | private boolean isProgressSuite; |
| | | |
| | | private BindRequest bindRequest = null; |
| | | |
| | | /** |
| | | * Defines the different line styles for output. |
| | | */ |
| | |
| | | } |
| | | |
| | | /** |
| | | * Returns the bind request used by this application. |
| | | * |
| | | * @return The bind request used by this application. |
| | | */ |
| | | public BindRequest getBindRequest() { |
| | | return bindRequest; |
| | | } |
| | | |
| | | /** |
| | | * Returns the application error stream. |
| | | * |
| | | * @return The application error stream. |
| | |
| | | throw new ClientException(ReturnCode.ERROR_USER_DATA, ERR_TRIES_LIMIT_REACHED.get(maxTries)); |
| | | } |
| | | |
| | | void setBindRequest(final BindRequest bindRequest) { |
| | | this.bindRequest = bindRequest; |
| | | } |
| | | |
| | | /** |
| | | * Inserts line breaks into the provided buffer to wrap text at no more than the specified column width (80). |
| | | * |
| | |
| | | <className>org/forgerock/opendj/ldap/Connections</className> |
| | | <differenceType>7005</differenceType> |
| | | <method>%regex[org\.forgerock\.opendj\.ldap\.ConnectionFactory newHeartBeatConnectionFactory\(org\.forgerock\.opendj\.ldap\.ConnectionFactory, long, java\.util\.concurrent\.TimeUnit, org\.forgerock\.opendj\.ldap\.requests\.SearchRequest(, java\.util\.concurrent\.ScheduledExecutorService)?\)]</method> |
| | | <to>%regex[org\.forgerock\.opendj\.ldap\.ConnectionFactory newHeartBeatConnectionFactory\(java\.lang\.String,\s*int(,\s*long,\s*long,\s*java\.util\.concurrent\.TimeUnit(,\s*org\.forgerock\.opendj\.ldap\.requests\.SearchRequest(,\s*java\.util\.concurrent\.ScheduledExecutorService)?)?)?\)]</to> |
| | | <justification>OPENDJ-1607 Merge HeartBeatConnectionFactory, AuthenticatedConnectionFactory and LDAPConnectionFactory</justification> |
| | | <to>%regex[org\.forgerock\.opendj\.ldap\.ConnectionFactory newHeartBeatConnectionFactory\(org\.forgerock\.opendj\.ldap\.ConnectionFactory,\s*long,\s*long,\s*java\.util\.concurrent\.TimeUnit(,\s*org\.forgerock\.opendj\.ldap\.requests\.SearchRequest(,\s*java\.util\.concurrent\.ScheduledExecutorService)?)?\)]</to> |
| | | <justification>OPENDJ-1058: Added a timeout parameter to actively shutdown dead connections</justification> |
| | | </difference> |
| | | <difference> |
| | | <className>org/forgerock/opendj/ldap/Connections</className> |
| | |
| | | <justification>OPENDJ-1270: Changed constructors to only accept InetSocketAddresses instead of more generic SocketAddress</justification> |
| | | </difference> |
| | | <difference> |
| | | <className>org/forgerock/opendj/ldap/LDAPListener</className> |
| | | <differenceType>7005</differenceType> |
| | | <method>%regex[LDAPListener\(java\.net\.SocketAddress, org\.forgerock\.opendj\.ldap\.ServerConnectionFactory(,\s*org\.forgerock\.opendj\.ldap\.LDAPListenerOptions)?\)]</method> |
| | | <to>%regex[LDAPListener\(java\.net\.InetSocketAddress,\s*org\.forgerock\.opendj\.ldap\.ServerConnectionFactory(,\s*org\.forgerock\.opendj\.ldap\.LDAPListenerOptions)?\)]</to> |
| | | <justification>OPENDJ-1270: Changed constructors to only accept InetSocketAddresses instead of more generic SocketAddress</justification> |
| | | </difference> |
| | | |
| | | <difference> |
| | | <className>%regex[org/forgerock/opendj/ldap/(LDAPOptions|LDAPListenerOptions)]</className> |
| | | <differenceType>7002</differenceType> |
| | | <method>%regex[org\.glassfish\.grizzly\.nio\.transport\.TCPNIOTransport getTCPNIOTransport\(\)]</method> |
| | |
| | | <method>*toNormalizedString()</method> |
| | | <to>*toIrreversibleNormalizedByteString()</to> |
| | | <justification>OPENDJ-1585 Function has been renamed to avoid abuse</justification> |
| | | </difference> |
| | | <difference> |
| | | <className>%regex[org/forgerock/opendj/ldap/(Authenticated|HeartBeat)ConnectionFactory]</className> |
| | | <differenceType>8001</differenceType> |
| | | <justification>OPENDJ-1607 Merge HeartBeatConnectionFactory, AuthenticatedConnectionFactory and LDAPConnectionFactory</justification> |
| | | </difference> |
| | | <difference> |
| | | <className>org/forgerock/opendj/ldap/AuthenticatedConnectionFactory$AuthenticatedConnection</className> |
| | | <differenceType>8001</differenceType> |
| | | <justification>OPENDJ-1607 Merge HeartBeatConnectionFactory, AuthenticatedConnectionFactory and LDAPConnectionFactory</justification> |
| | | </difference> |
| | | <difference> |
| | | <className>org/forgerock/opendj/ldap/Connections</className> |
| | | <differenceType>7002</differenceType> |
| | | <method>%regex[org.forgerock.opendj.ldap.ConnectionFactory new(HeartBeat|Authenticated)ConnectionFactory\(org.forgerock.opendj.ldap.ConnectionFactory(.)*\)]</method> |
| | | <justification>OPENDJ-1607 Merge HeartBeatConnectionFactory, AuthenticatedConnectionFactory and LDAPConnectionFactory</justification> |
| | | </difference> |
| | | <difference> |
| | | <className>org/forgerock/opendj/ldap/LDAPConnectionFactory</className> |
| | | <differenceType>7002</differenceType> |
| | | <method>LDAPConnectionFactory(java.lang.String, int)</method> |
| | | <justification>OPENDJ-1607 Merge HeartBeatConnectionFactory, AuthenticatedConnectionFactory and LDAPConnectionFactory</justification> |
| | | </difference> |
| | | <difference> |
| | | <className>org/forgerock/opendj/ldap/LDAPListener</className> |
| | | <differenceType>7002</differenceType> |
| | | <method>%regex[LDAPListener\((int|java\.net\.SocketAddress),\s*org\.forgerock\.opendj\.ldap\.ServerConnectionFactory(,\s*org\.forgerock\.opendj\.ldap\.LDAPListenerOptions)?\)]</method> |
| | | <justification>OPENDJ-1607 Merge HeartBeatConnectionFactory, AuthenticatedConnectionFactory and LDAPConnectionFactory</justification> |
| | | </difference> |
| | | <difference> |
| | | <className>org/forgerock/opendj/ldap/LDAPListener</className> |
| | | <differenceType>7002</differenceType> |
| | | <method>%regex[LDAPListener\(java.lang.String,\s*int,\s*org\.forgerock\.opendj\.ldap\.ServerConnectionFactory(,\s*org\.forgerock\.opendj\.ldap\.LDAPListenerOptions)?\)]</method> |
| | | <justification>OPENDJ-1607 Merge HeartBeatConnectionFactory, AuthenticatedConnectionFactory and LDAPConnectionFactory</justification> |
| | | </difference> |
| | | <difference> |
| | | <className>org/forgerock/opendj/ldap/LDAPListener</className> |
| | | <differenceType>7009</differenceType> |
| | | <method>LDAPListener(int, org.forgerock.opendj.ldap.ServerConnectionFactory, org.forgerock.opendj.ldap.LDAPListenerOptions)</method> |
| | | <justification>OPENDJ-1607 Merge HeartBeatConnectionFactory, AuthenticatedConnectionFactory and LDAPConnectionFactory</justification> |
| | | </difference> |
| | | <difference> |
| | | <className>org/forgerock/opendj/ldap/LDAPListener</className> |
| | | <differenceType>7005</differenceType> |
| | | <method>LDAPListener(int, org.forgerock.opendj.ldap.ServerConnectionFactory, org.forgerock.opendj.ldap.LDAPListenerOptions)</method> |
| | | <to>LDAPListener(*)</to> |
| | | <justification>OPENDJ-1607 Merge HeartBeatConnectionFactory, AuthenticatedConnectionFactory and LDAPConnectionFactory</justification> |
| | | </difference> |
| | | <difference> |
| | | <className>org/forgerock/opendj/ldap/LDAPConnectionFactory</className> |
| | | <differenceType>7009</differenceType> |
| | | <method>LDAPConnectionFactory(java.lang.String, int, org.forgerock.opendj.ldap.LDAPOptions)</method> |
| | | <justification>OPENDJ-1607 Merge HeartBeatConnectionFactory, AuthenticatedConnectionFactory and LDAPConnectionFactory</justification> |
| | | </difference> |
| | | <difference> |
| | | <className>%regex[org/forgerock/opendj/ldap/schema/Schema(Builder)?]</className> |
| New file |
| | |
| | | /* |
| | | * 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 2009-2010 Sun Microsystems, Inc. |
| | | * Portions Copyright 2011-2014 ForgeRock AS. |
| | | */ |
| | | |
| | | package org.forgerock.opendj.ldap; |
| | | |
| | | import java.util.concurrent.atomic.AtomicReference; |
| | | |
| | | import org.forgerock.opendj.ldap.requests.BindRequest; |
| | | import org.forgerock.opendj.ldap.responses.BindResult; |
| | | import org.forgerock.util.promise.AsyncFunction; |
| | | import org.forgerock.util.promise.Function; |
| | | import org.forgerock.util.promise.Promise; |
| | | |
| | | import static org.forgerock.util.Utils.*; |
| | | |
| | | /** |
| | | * An authenticated connection factory can be used to create pre-authenticated |
| | | * connections to a Directory Server. |
| | | * <p> |
| | | * The connections returned by an authenticated connection factory support all |
| | | * operations with the exception of Bind requests. Attempts to perform a Bind |
| | | * will result in an {@code UnsupportedOperationException}. |
| | | * <p> |
| | | * If the Bind request fails for some reason (e.g. invalid credentials), then |
| | | * the connection attempt will fail and an {@link LdapException} will be thrown. |
| | | */ |
| | | final class AuthenticatedConnectionFactory implements ConnectionFactory { |
| | | |
| | | /** |
| | | * An authenticated connection supports all operations except Bind |
| | | * operations. |
| | | */ |
| | | public static final class AuthenticatedConnection extends AbstractConnectionWrapper<Connection> { |
| | | |
| | | private AuthenticatedConnection(final Connection connection) { |
| | | super(connection); |
| | | } |
| | | |
| | | /** |
| | | * Bind operations are not supported by pre-authenticated connections. |
| | | * These methods will always throw {@code UnsupportedOperationException}. |
| | | */ |
| | | public LdapPromise<BindResult> bindAsync(final BindRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler, |
| | | final ResultHandler<? super BindResult> resultHandler) { |
| | | throw new UnsupportedOperationException(); |
| | | } |
| | | |
| | | @Override |
| | | public BindResult bind(BindRequest request) throws LdapException { |
| | | throw new UnsupportedOperationException(); |
| | | } |
| | | |
| | | @Override |
| | | public BindResult bind(String name, char[] password) throws LdapException { |
| | | throw new UnsupportedOperationException(); |
| | | } |
| | | |
| | | @Override |
| | | public String toString() { |
| | | StringBuilder builder = new StringBuilder(); |
| | | builder.append("AuthenticatedConnection("); |
| | | builder.append(connection); |
| | | builder.append(')'); |
| | | return builder.toString(); |
| | | } |
| | | |
| | | } |
| | | |
| | | private final BindRequest request; |
| | | private final ConnectionFactory parentFactory; |
| | | |
| | | /** |
| | | * Creates a new authenticated connection factory which will obtain |
| | | * connections using the provided connection factory and immediately perform |
| | | * the provided Bind request. |
| | | * |
| | | * @param factory |
| | | * The connection factory to use for connecting to the Directory |
| | | * Server. |
| | | * @param request |
| | | * The Bind request to use for authentication. |
| | | */ |
| | | AuthenticatedConnectionFactory(final ConnectionFactory factory, final BindRequest request) { |
| | | this.parentFactory = factory; |
| | | |
| | | // FIXME: should do a defensive copy. |
| | | this.request = request; |
| | | } |
| | | |
| | | @Override |
| | | public void close() { |
| | | // Delegate. |
| | | parentFactory.close(); |
| | | } |
| | | |
| | | @Override |
| | | public Connection getConnection() throws LdapException { |
| | | final Connection connection = parentFactory.getConnection(); |
| | | boolean bindSucceeded = false; |
| | | try { |
| | | connection.bind(request); |
| | | bindSucceeded = true; |
| | | } finally { |
| | | if (!bindSucceeded) { |
| | | connection.close(); |
| | | } |
| | | } |
| | | |
| | | /* |
| | | * If the bind didn't succeed then an exception will have been thrown |
| | | * and this line will not be reached. |
| | | */ |
| | | return new AuthenticatedConnection(connection); |
| | | } |
| | | |
| | | @Override |
| | | public Promise<Connection, LdapException> getConnectionAsync() { |
| | | final AtomicReference<Connection> connectionHolder = new AtomicReference<Connection>(); |
| | | return parentFactory.getConnectionAsync() |
| | | .thenAsync( |
| | | new AsyncFunction<Connection, BindResult, LdapException>() { |
| | | @Override |
| | | public Promise<BindResult, LdapException> apply(final Connection connection) |
| | | throws LdapException { |
| | | connectionHolder.set(connection); |
| | | return connection.bindAsync(request); |
| | | } |
| | | } |
| | | ).then( |
| | | new Function<BindResult, Connection, LdapException>() { |
| | | @Override |
| | | public Connection apply(BindResult result) throws LdapException { |
| | | return new AuthenticatedConnection(connectionHolder.get()); |
| | | } |
| | | }, |
| | | new Function<LdapException, Connection, LdapException>() { |
| | | @Override |
| | | public Connection apply(LdapException error) throws LdapException { |
| | | closeSilently(connectionHolder.get()); |
| | | throw error; |
| | | } |
| | | } |
| | | ); |
| | | } |
| | | |
| | | @Override |
| | | public String toString() { |
| | | final StringBuilder builder = new StringBuilder(); |
| | | builder.append("AuthenticatedConnectionFactory("); |
| | | builder.append(parentFactory); |
| | | builder.append(", "); |
| | | builder.append(request); |
| | | builder.append(')'); |
| | | return builder.toString(); |
| | | } |
| | | } |
| | |
| | | |
| | | package org.forgerock.opendj.ldap; |
| | | |
| | | import java.io.IOException; |
| | | import static org.forgerock.opendj.ldap.RequestHandlerFactoryAdapter.adaptRequestHandler; |
| | | |
| | | import java.net.InetAddress; |
| | | import java.net.InetSocketAddress; |
| | | import java.util.concurrent.ScheduledExecutorService; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | import org.forgerock.opendj.ldap.requests.BindRequest; |
| | | import org.forgerock.opendj.ldap.requests.SearchRequest; |
| | | import org.forgerock.util.Reject; |
| | | import org.forgerock.util.promise.Promise; |
| | | |
| | | import static org.forgerock.opendj.ldap.RequestHandlerFactoryAdapter.*; |
| | | |
| | | /** |
| | | * This class contains methods for creating and manipulating connection |
| | | * factories and connections. |
| | | */ |
| | | public final class Connections { |
| | | /** |
| | | * Creates a new authenticated connection factory which will obtain |
| | | * connections using the provided connection factory and immediately perform |
| | | * the provided Bind request. |
| | | * <p> |
| | | * The connections returned by an authenticated connection factory support |
| | | * all operations with the exception of Bind requests. Attempts to perform a |
| | | * Bind will result in an {@code UnsupportedOperationException}. |
| | | * <p> |
| | | * If the Bind request fails for some reason (e.g. invalid credentials), |
| | | * then the connection attempt will fail and an {@link LdapException} |
| | | * will be thrown. |
| | | * |
| | | * @param factory |
| | | * The connection factory to use for connecting to the Directory |
| | | * Server. |
| | | * @param request |
| | | * The Bind request to use for authentication. |
| | | * @return The new connection pool. |
| | | * @throws NullPointerException |
| | | * If {@code factory} or {@code request} was {@code null}. |
| | | */ |
| | | public static ConnectionFactory newAuthenticatedConnectionFactory( |
| | | final ConnectionFactory factory, final BindRequest request) { |
| | | Reject.ifNull(factory, request); |
| | | |
| | | return new AuthenticatedConnectionFactory(factory, request); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new connection pool which creates new connections as needed |
| | |
| | | } |
| | | |
| | | /** |
| | | * Creates a new heart-beat connection factory which will create connections |
| | | * using the provided connection factory and periodically ping any created |
| | | * connections in order to detect that they are still alive every 10 seconds |
| | | * using the default scheduler. Connections will be marked as having failed |
| | | * if a heart-beat takes longer than 3 seconds. |
| | | * |
| | | * @param factory |
| | | * The connection factory to use for creating connections. |
| | | * @return The new heart-beat connection factory. |
| | | * @throws NullPointerException |
| | | * If {@code factory} was {@code null}. |
| | | */ |
| | | public static ConnectionFactory newHeartBeatConnectionFactory(final ConnectionFactory factory) { |
| | | return new HeartBeatConnectionFactory(factory, 10, 3, TimeUnit.SECONDS, null, null); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new heart-beat connection factory which will create connections |
| | | * using the provided connection factory and periodically ping any created |
| | | * connections in order to detect that they are still alive using the |
| | | * specified frequency and the default scheduler. |
| | | * |
| | | * @param factory |
| | | * The connection factory to use for creating connections. |
| | | * @param interval |
| | | * The interval between keepalive pings. |
| | | * @param timeout |
| | | * The heart-beat timeout after which a connection will be marked |
| | | * as failed. |
| | | * @param unit |
| | | * The time unit for the interval between keepalive pings. |
| | | * @return The new heart-beat connection factory. |
| | | * @throws IllegalArgumentException |
| | | * If {@code interval} was negative. |
| | | * @throws NullPointerException |
| | | * If {@code factory} or {@code unit} was {@code null}. |
| | | */ |
| | | public static ConnectionFactory newHeartBeatConnectionFactory(final ConnectionFactory factory, |
| | | final long interval, final long timeout, final TimeUnit unit) { |
| | | return new HeartBeatConnectionFactory(factory, interval, timeout, unit, null, null); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new heart-beat connection factory which will create connections |
| | | * using the provided connection factory and periodically ping any created |
| | | * connections using the specified search request in order to detect that |
| | | * they are still alive. |
| | | * |
| | | * @param factory |
| | | * The connection factory to use for creating connections. |
| | | * @param interval |
| | | * The interval between keepalive pings. |
| | | * @param timeout |
| | | * The heart-beat timeout after which a connection will be marked |
| | | * as failed. |
| | | * @param unit |
| | | * The time unit for the interval between keepalive pings. |
| | | * @param heartBeat |
| | | * The search request to use for keepalive pings. |
| | | * @return The new heart-beat connection factory. |
| | | * @throws IllegalArgumentException |
| | | * If {@code interval} was negative. |
| | | * @throws NullPointerException |
| | | * If {@code factory}, {@code unit}, or {@code heartBeat} was |
| | | * {@code null}. |
| | | */ |
| | | public static ConnectionFactory newHeartBeatConnectionFactory(final ConnectionFactory factory, |
| | | final long interval, final long timeout, final TimeUnit unit, |
| | | final SearchRequest heartBeat) { |
| | | return new HeartBeatConnectionFactory(factory, interval, timeout, unit, heartBeat, null); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new heart-beat connection factory which will create connections |
| | | * using the provided connection factory and periodically ping any created |
| | | * connections using the specified search request in order to detect that |
| | | * they are still alive. |
| | | * |
| | | * @param factory |
| | | * The connection factory to use for creating connections. |
| | | * @param interval |
| | | * The interval between keepalive pings. |
| | | * @param timeout |
| | | * The heart-beat timeout after which a connection will be marked |
| | | * as failed. |
| | | * @param unit |
| | | * The time unit for the interval between keepalive pings. |
| | | * @param heartBeat |
| | | * The search request to use for keepalive pings. |
| | | * @param scheduler |
| | | * The scheduler which should for periodically sending keepalive |
| | | * pings. |
| | | * @return The new heart-beat connection factory. |
| | | * @throws IllegalArgumentException |
| | | * If {@code interval} was negative. |
| | | * @throws NullPointerException |
| | | * If {@code factory}, {@code unit}, or {@code heartBeat} was |
| | | * {@code null}. |
| | | */ |
| | | public static ConnectionFactory newHeartBeatConnectionFactory(final ConnectionFactory factory, |
| | | final long interval, final long timeout, final TimeUnit unit, |
| | | final SearchRequest heartBeat, final ScheduledExecutorService scheduler) { |
| | | return new HeartBeatConnectionFactory(factory, interval, timeout, unit, heartBeat, |
| | | scheduler); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new internal client connection which will route requests to the |
| | | * provided {@code RequestHandler}. |
| | | * <p> |
| | |
| | | } |
| | | |
| | | /** |
| | | * Creates a new LDAP connection factory which can be used to create LDAP |
| | | * connections to the Directory Server at the provided host and port |
| | | * number. |
| | | * |
| | | * @param host |
| | | * The host name. |
| | | * @param port |
| | | * The port number. |
| | | * @return The connection factory to use for creating connections. |
| | | */ |
| | | public static LDAPConnectionFactory newLDAPConnectionFactory(final String host, final int port) { |
| | | return new LDAPConnectionFactory(host, port, new LDAPOptions()); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new LDAP connection factory which can be used to create LDAP |
| | | * connections to the Directory Server at the provided host and port |
| | | * number. |
| | | * |
| | | * @param host |
| | | * The host name. |
| | | * @param port |
| | | * The port number. |
| | | * @param options |
| | | * The LDAP options to use when creating connections. |
| | | * @return The connection factory to use for creating connections. |
| | | */ |
| | | public static LDAPConnectionFactory newLDAPConnectionFactory(final String host, final int port, |
| | | final LDAPOptions options) { |
| | | return new LDAPConnectionFactory(host, port, options); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new LDAP listener implementation which will listen for LDAP |
| | | * client connections at the provided address. |
| | | * |
| | | * @param port |
| | | * The port to listen on. |
| | | * @param factory |
| | | * The server connection factory which will be used to create |
| | | * server connections. |
| | | * @throws IOException |
| | | * If an error occurred while trying to listen on the provided |
| | | * address. |
| | | * @throws NullPointerException |
| | | * If {code factory} was {@code null}. |
| | | * @return The LDAP listener implementation which will listen for LDAP client connections. |
| | | */ |
| | | public static LDAPListener newLDAPListener(final int port, |
| | | final ServerConnectionFactory<LDAPClientContext, Integer> factory) throws IOException { |
| | | return newLDAPListener(null, port, factory, new LDAPListenerOptions()); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new LDAP listener implementation which will listen for LDAP |
| | | * client connections at the provided address. |
| | | * |
| | | * @param port |
| | | * The port to listen on. |
| | | * @param factory |
| | | * The server connection factory which will be used to create |
| | | * server connections. |
| | | * @param options |
| | | * The LDAP listener options. |
| | | * @throws IOException |
| | | * If an error occurred while trying to listen on the provided |
| | | * address. |
| | | * @throws NullPointerException |
| | | * If {code factory} or {@code options} was {@code null}. |
| | | * @return The LDAP listener implementation which will listen for LDAP client connections. |
| | | */ |
| | | public static LDAPListener newLDAPListener(final int port, |
| | | final ServerConnectionFactory<LDAPClientContext, Integer> factory, |
| | | final LDAPListenerOptions options) throws IOException { |
| | | return newLDAPListener(null, port, factory, options); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new LDAP listener implementation which will listen for LDAP |
| | | * client connections at the provided address. |
| | | * |
| | | * @param host |
| | | * The address to listen on. |
| | | * @param port |
| | | * The port to listen on. |
| | | * @param factory |
| | | * The server connection factory which will be used to create |
| | | * server connections. |
| | | * @throws IOException |
| | | * If an error occurred while trying to listen on the provided |
| | | * address. |
| | | * @throws NullPointerException |
| | | * If {@code host} or {code factory} was {@code null}. |
| | | * @return The LDAP listener implementation which will listen for LDAP client connections. |
| | | */ |
| | | public static LDAPListener newLDAPListener(final String host, final int port, |
| | | final ServerConnectionFactory<LDAPClientContext, Integer> factory) throws IOException { |
| | | return newLDAPListener(host, port, factory, new LDAPListenerOptions()); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new LDAP listener implementation which will listen for LDAP |
| | | * client connections at the provided address. |
| | | * |
| | | * @param host |
| | | * The address to listen on. |
| | | * @param port |
| | | * The port to listen on. |
| | | * @param factory |
| | | * The server connection factory which will be used to create |
| | | * server connections. |
| | | * @param options |
| | | * The LDAP listener options. |
| | | * @throws IOException |
| | | * If an error occurred while trying to listen on the provided |
| | | * address. |
| | | * @throws NullPointerException |
| | | * If {@code host}, {code factory}, or {@code options} was |
| | | * {@code null}. |
| | | * @return The LDAP listener implementation which will listen for LDAP client connections. |
| | | */ |
| | | public static LDAPListener newLDAPListener(final String host, final int port, |
| | | final ServerConnectionFactory<LDAPClientContext, Integer> factory, |
| | | final LDAPListenerOptions options) throws IOException { |
| | | return newLDAPListener(host != null ? new InetSocketAddress(host, port) : new InetSocketAddress(port), |
| | | factory, options); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new LDAP listener implementation which will listen for LDAP |
| | | * client connections at the provided address. |
| | | * |
| | | * @param address |
| | | * The address to listen on. |
| | | * @param factory |
| | | * The server connection factory which will be used to create |
| | | * server connections. |
| | | * @throws IOException |
| | | * If an error occurred while trying to listen on the provided |
| | | * address. |
| | | * @throws NullPointerException |
| | | * If {@code address} or {code factory} was {@code null}. |
| | | * @return The LDAP listener implementation which will listen for LDAP client connections. |
| | | */ |
| | | public static LDAPListener newLDAPListener(final InetSocketAddress address, |
| | | final ServerConnectionFactory<LDAPClientContext, Integer> factory) throws IOException { |
| | | return newLDAPListener(address, factory, new LDAPListenerOptions()); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new LDAP listener implementation which will listen for LDAP |
| | | * client connections at the provided address. |
| | | * |
| | | * @param address |
| | | * The address to listen on. |
| | | * @param factory |
| | | * The server connection factory which will be used to create |
| | | * server connections. |
| | | * @param options |
| | | * The LDAP listener options. |
| | | * @throws IOException |
| | | * If an error occurred while trying to listen on the provided |
| | | * address. |
| | | * @throws NullPointerException |
| | | * If {@code address}, {code factory}, or {@code options} was |
| | | * {@code null}. |
| | | * @return The LDAP listener implementation which will listen for LDAP client connections. |
| | | */ |
| | | public static LDAPListener newLDAPListener(final InetSocketAddress address, |
| | | final ServerConnectionFactory<LDAPClientContext, Integer> factory, |
| | | final LDAPListenerOptions options) throws IOException { |
| | | return new LDAPListener(address, factory, options); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new load balancer which will obtain connections using the |
| | | * provided load balancing algorithm. |
| | | * |
| New file |
| | |
| | | /* |
| | | * 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 2009-2010 Sun Microsystems, Inc. |
| | | * Portions Copyright 2011-2014 ForgeRock AS. |
| | | */ |
| | | package org.forgerock.opendj.ldap; |
| | | |
| | | |
| | | |
| | | import java.util.LinkedList; |
| | | import java.util.List; |
| | | import java.util.Queue; |
| | | import java.util.concurrent.ConcurrentLinkedQueue; |
| | | import java.util.concurrent.ScheduledExecutorService; |
| | | import java.util.concurrent.ScheduledFuture; |
| | | import java.util.concurrent.TimeUnit; |
| | | import java.util.concurrent.TimeoutException; |
| | | import java.util.concurrent.atomic.AtomicBoolean; |
| | | import java.util.concurrent.atomic.AtomicInteger; |
| | | import java.util.concurrent.atomic.AtomicReference; |
| | | import java.util.concurrent.locks.AbstractQueuedSynchronizer; |
| | | |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | | import org.forgerock.i18n.slf4j.LocalizedLogger; |
| | | import org.forgerock.opendj.ldap.requests.AbandonRequest; |
| | | import org.forgerock.opendj.ldap.requests.AddRequest; |
| | | import org.forgerock.opendj.ldap.requests.BindRequest; |
| | | import org.forgerock.opendj.ldap.requests.CompareRequest; |
| | | import org.forgerock.opendj.ldap.requests.DeleteRequest; |
| | | import org.forgerock.opendj.ldap.requests.ExtendedRequest; |
| | | import org.forgerock.opendj.ldap.requests.ModifyDNRequest; |
| | | import org.forgerock.opendj.ldap.requests.ModifyRequest; |
| | | import org.forgerock.opendj.ldap.requests.Requests; |
| | | import org.forgerock.opendj.ldap.requests.SearchRequest; |
| | | import org.forgerock.opendj.ldap.requests.StartTLSExtendedRequest; |
| | | import org.forgerock.opendj.ldap.requests.UnbindRequest; |
| | | import org.forgerock.opendj.ldap.responses.BindResult; |
| | | import org.forgerock.opendj.ldap.responses.CompareResult; |
| | | import org.forgerock.opendj.ldap.responses.ExtendedResult; |
| | | import org.forgerock.opendj.ldap.responses.Result; |
| | | import org.forgerock.opendj.ldap.responses.SearchResultEntry; |
| | | import org.forgerock.opendj.ldap.responses.SearchResultReference; |
| | | import org.forgerock.opendj.ldap.spi.ConnectionState; |
| | | import org.forgerock.opendj.ldap.spi.LdapPromiseImpl; |
| | | import org.forgerock.util.Reject; |
| | | import org.forgerock.util.promise.AsyncFunction; |
| | | import org.forgerock.util.promise.FailureHandler; |
| | | import org.forgerock.util.promise.Promise; |
| | | import org.forgerock.util.promise.PromiseImpl; |
| | | import org.forgerock.util.promise.SuccessHandler; |
| | | |
| | | import com.forgerock.opendj.util.ReferenceCountedObject; |
| | | import org.forgerock.util.time.TimeService; |
| | | |
| | | import static org.forgerock.opendj.ldap.LdapException.*; |
| | | import static org.forgerock.opendj.ldap.spi.LdapPromiseImpl.*; |
| | | import static org.forgerock.opendj.ldap.spi.LdapPromises.*; |
| | | import static org.forgerock.util.Utils.*; |
| | | |
| | | import static com.forgerock.opendj.ldap.CoreMessages.*; |
| | | import static com.forgerock.opendj.util.StaticUtils.*; |
| | | |
| | | /** |
| | | * An heart beat connection factory can be used to create connections that sends |
| | | * a periodic search request to a Directory Server. |
| | | * <p> |
| | | * Before returning new connections to the application this factory will first |
| | | * send an initial heart beat in order to determine that the remote server is |
| | | * responsive. If the heart beat fails or times out then the connection is |
| | | * closed immediately and an error returned to the client. |
| | | * <p> |
| | | * Once a connection has been established successfully (including the initial |
| | | * heart beat), this factory will periodically send heart beats on the |
| | | * connection based on the configured heart beat interval. If the heart beat |
| | | * times out then the server is assumed to be down and an appropriate |
| | | * {@link ConnectionException} generated and published to any registered |
| | | * {@link ConnectionEventListener}s. Note however, that heart beats will only be |
| | | * sent when the connection is determined to be reasonably idle: there is no |
| | | * point in sending heart beats if the connection has recently received a |
| | | * response. A connection is deemed to be idle if no response has been received |
| | | * during a period equivalent to half the heart beat interval. |
| | | * <p> |
| | | * The LDAP protocol specifically precludes clients from performing operations |
| | | * while bind or startTLS requests are being performed. Likewise, a bind or |
| | | * startTLS request will cause active operations to be aborted. This factory |
| | | * coordinates heart beats with bind or startTLS requests, ensuring that they |
| | | * are not performed concurrently. Specifically, bind and startTLS requests are |
| | | * queued up while a heart beat is pending, and heart beats are not sent at all |
| | | * while there are pending bind or startTLS requests. |
| | | */ |
| | | final class HeartBeatConnectionFactory implements ConnectionFactory { |
| | | /** |
| | | * A connection that sends heart beats and supports all operations. |
| | | */ |
| | | private final class ConnectionImpl extends AbstractAsynchronousConnection implements ConnectionEventListener { |
| | | |
| | | private class LdapPromiseImplWrapper<R> extends LdapPromiseImpl<R> { |
| | | protected LdapPromiseImplWrapper(final LdapPromise<R> wrappedPromise) { |
| | | super(new PromiseImpl<R, LdapException>() { |
| | | @Override |
| | | protected LdapException tryCancel(boolean mayInterruptIfRunning) { |
| | | /* |
| | | * FIXME: if the inner cancel succeeds then this promise will be |
| | | * completed and we can never indicate that this cancel request |
| | | * has succeeded. |
| | | */ |
| | | wrappedPromise.cancel(mayInterruptIfRunning); |
| | | return null; |
| | | } |
| | | }, wrappedPromise.getRequestID()); |
| | | } |
| | | } |
| | | |
| | | /** The wrapped connection. */ |
| | | private final Connection connection; |
| | | |
| | | /** |
| | | * List of pending Bind or StartTLS requests which must be invoked once |
| | | * the current heart beat completes. |
| | | */ |
| | | private final Queue<Runnable> pendingBindOrStartTLSRequests = |
| | | new ConcurrentLinkedQueue<Runnable>(); |
| | | |
| | | /** |
| | | * List of pending responses for all active operations. These will be |
| | | * signalled if no heart beat is detected within the permitted timeout |
| | | * period. |
| | | */ |
| | | private final Queue<ResultHandler<?>> pendingResults = |
| | | new ConcurrentLinkedQueue<ResultHandler<?>>(); |
| | | |
| | | /** Internal connection state. */ |
| | | private final ConnectionState state = new ConnectionState(); |
| | | |
| | | /** Coordinates heart-beats with Bind and StartTLS requests. */ |
| | | private final Sync sync = new Sync(); |
| | | |
| | | /** |
| | | * Timestamp of last response received (any response, not just heart |
| | | * beats). |
| | | */ |
| | | private volatile long lastResponseTimestamp = timeService.now(); // Assume valid at creation. |
| | | |
| | | private ConnectionImpl(final Connection connection) { |
| | | this.connection = connection; |
| | | connection.addConnectionEventListener(this); |
| | | } |
| | | |
| | | @Override |
| | | public LdapPromise<Void> abandonAsync(final AbandonRequest request) { |
| | | return connection.abandonAsync(request); |
| | | } |
| | | |
| | | @Override |
| | | public LdapPromise<Result> addAsync(final AddRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | if (checkState()) { |
| | | return timestampPromise(connection.addAsync(request, intermediateResponseHandler)); |
| | | } else { |
| | | return newConnectionErrorPromise(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void addConnectionEventListener(final ConnectionEventListener listener) { |
| | | state.addConnectionEventListener(listener); |
| | | } |
| | | |
| | | @Override |
| | | public LdapPromise<BindResult> bindAsync(final BindRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | if (checkState()) { |
| | | if (sync.tryLockShared()) { |
| | | // Fast path |
| | | return timestampBindOrStartTLSPromise(connection.bindAsync(request, intermediateResponseHandler)); |
| | | } else { |
| | | return enqueueBindOrStartTLSPromise(new AsyncFunction<Void, BindResult, LdapException>() { |
| | | @Override |
| | | public Promise<BindResult, LdapException> apply(Void value) throws LdapException { |
| | | return timestampBindOrStartTLSPromise(connection.bindAsync(request, |
| | | intermediateResponseHandler)); |
| | | } |
| | | }); |
| | | } |
| | | } else { |
| | | return newConnectionErrorPromise(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void close() { |
| | | handleConnectionClosed(); |
| | | connection.close(); |
| | | } |
| | | |
| | | @Override |
| | | public void close(final UnbindRequest request, final String reason) { |
| | | handleConnectionClosed(); |
| | | connection.close(request, reason); |
| | | } |
| | | |
| | | @Override |
| | | public LdapPromise<CompareResult> compareAsync(final CompareRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | if (checkState()) { |
| | | return timestampPromise(connection.compareAsync(request, intermediateResponseHandler)); |
| | | } else { |
| | | return newConnectionErrorPromise(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public LdapPromise<Result> deleteAsync(final DeleteRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | if (checkState()) { |
| | | return timestampPromise(connection.deleteAsync(request, intermediateResponseHandler)); |
| | | } else { |
| | | return newConnectionErrorPromise(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public <R extends ExtendedResult> LdapPromise<R> extendedRequestAsync(final ExtendedRequest<R> request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | if (checkState()) { |
| | | if (isStartTLSRequest(request)) { |
| | | if (sync.tryLockShared()) { |
| | | // Fast path |
| | | return timestampBindOrStartTLSPromise(connection.extendedRequestAsync(request, |
| | | intermediateResponseHandler)); |
| | | } else { |
| | | return enqueueBindOrStartTLSPromise(new AsyncFunction<Void, R, LdapException>() { |
| | | @Override |
| | | public Promise<R, LdapException> apply(Void value) throws LdapException { |
| | | return timestampBindOrStartTLSPromise(connection.extendedRequestAsync( |
| | | request, intermediateResponseHandler)); |
| | | } |
| | | }); |
| | | } |
| | | } else { |
| | | return timestampPromise(connection.extendedRequestAsync(request, intermediateResponseHandler)); |
| | | } |
| | | } else { |
| | | return newConnectionErrorPromise(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void handleConnectionClosed() { |
| | | if (state.notifyConnectionClosed()) { |
| | | failPendingResults(newLdapException(ResultCode.CLIENT_SIDE_USER_CANCELLED, |
| | | HBCF_CONNECTION_CLOSED_BY_CLIENT.get())); |
| | | synchronized (validConnections) { |
| | | connection.removeConnectionEventListener(this); |
| | | validConnections.remove(this); |
| | | if (validConnections.isEmpty()) { |
| | | // This is the last active connection, so stop the heartbeat. |
| | | heartBeatFuture.cancel(false); |
| | | } |
| | | } |
| | | releaseScheduler(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void handleConnectionError(final boolean isDisconnectNotification, final LdapException error) { |
| | | if (state.notifyConnectionError(isDisconnectNotification, error)) { |
| | | failPendingResults(error); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void handleUnsolicitedNotification(final ExtendedResult notification) { |
| | | timestamp(notification); |
| | | state.notifyUnsolicitedNotification(notification); |
| | | } |
| | | |
| | | @Override |
| | | public boolean isClosed() { |
| | | return state.isClosed(); |
| | | } |
| | | |
| | | @Override |
| | | public boolean isValid() { |
| | | return state.isValid() && connection.isValid(); |
| | | } |
| | | |
| | | @Override |
| | | public LdapPromise<Result> modifyAsync(final ModifyRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | if (checkState()) { |
| | | return timestampPromise(connection.modifyAsync(request, intermediateResponseHandler)); |
| | | } else { |
| | | return newConnectionErrorPromise(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public LdapPromise<Result> modifyDNAsync(final ModifyDNRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | if (checkState()) { |
| | | return timestampPromise(connection.modifyDNAsync(request, intermediateResponseHandler)); |
| | | } else { |
| | | return newConnectionErrorPromise(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void removeConnectionEventListener(final ConnectionEventListener listener) { |
| | | state.removeConnectionEventListener(listener); |
| | | } |
| | | |
| | | @Override |
| | | public LdapPromise<Result> searchAsync(final SearchRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler, |
| | | final SearchResultHandler searchHandler) { |
| | | if (checkState()) { |
| | | final AtomicBoolean searchDone = new AtomicBoolean(); |
| | | final SearchResultHandler entryHandler = new SearchResultHandler() { |
| | | @Override |
| | | public synchronized boolean handleEntry(SearchResultEntry entry) { |
| | | if (!searchDone.get()) { |
| | | timestamp(entry); |
| | | if (searchHandler != null) { |
| | | searchHandler.handleEntry(entry); |
| | | } |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | public synchronized boolean handleReference(SearchResultReference reference) { |
| | | if (!searchDone.get()) { |
| | | timestamp(reference); |
| | | if (searchHandler != null) { |
| | | searchHandler.handleReference(reference); |
| | | } |
| | | } |
| | | return true; |
| | | } |
| | | }; |
| | | |
| | | return timestampPromise(connection.searchAsync(request, |
| | | intermediateResponseHandler, entryHandler).onSuccessOrFailure(new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | searchDone.getAndSet(true); |
| | | } |
| | | })); |
| | | } else { |
| | | return newConnectionErrorPromise(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public String toString() { |
| | | final StringBuilder builder = new StringBuilder(); |
| | | builder.append("HeartBeatConnection("); |
| | | builder.append(connection); |
| | | builder.append(')'); |
| | | return builder.toString(); |
| | | } |
| | | |
| | | private void checkForHeartBeat() { |
| | | if (sync.isHeld()) { |
| | | /* |
| | | * A heart beat or bind/startTLS is still in progress, but it |
| | | * should have completed by now. Let's avoid aggressively |
| | | * terminating the connection, because the heart beat may simply |
| | | * have been delayed by a sudden surge of activity. Therefore, |
| | | * only flag the connection as failed if no activity has been |
| | | * seen on the connection since the heart beat was sent. |
| | | */ |
| | | final long currentTimeMillis = timeService.now(); |
| | | if (lastResponseTimestamp < (currentTimeMillis - timeoutMS)) { |
| | | logger.warn(LocalizableMessage.raw("No heartbeat detected for connection '%s'", connection)); |
| | | handleConnectionError(false, newHeartBeatTimeoutError()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private boolean checkState() { |
| | | return state.getConnectionError() == null; |
| | | } |
| | | |
| | | private void failPendingResults(final LdapException error) { |
| | | /* |
| | | * Peek instead of pool because notification is responsible for |
| | | * removing the element from the queue. |
| | | */ |
| | | ResultHandler<?> pendingResult; |
| | | while ((pendingResult = pendingResults.peek()) != null) { |
| | | pendingResult.handleError(error); |
| | | } |
| | | } |
| | | |
| | | private void flushPendingBindOrStartTLSRequests() { |
| | | if (!pendingBindOrStartTLSRequests.isEmpty()) { |
| | | /* |
| | | * The pending requests will acquire the shared lock, but we take |
| | | * it here anyway to ensure that pending requests do not get blocked. |
| | | */ |
| | | if (sync.tryLockShared()) { |
| | | try { |
| | | Runnable pendingRequest; |
| | | while ((pendingRequest = pendingBindOrStartTLSRequests.poll()) != null) { |
| | | // Dispatch the waiting request. This will not block. |
| | | pendingRequest.run(); |
| | | } |
| | | } finally { |
| | | sync.unlockShared(); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | private boolean isStartTLSRequest(final ExtendedRequest<?> request) { |
| | | return request.getOID().equals(StartTLSExtendedRequest.OID); |
| | | } |
| | | |
| | | private <R> LdapPromise<R> newConnectionErrorPromise() { |
| | | return newFailedLdapPromise(state.getConnectionError()); |
| | | } |
| | | |
| | | private void releaseBindOrStartTLSLock() { |
| | | sync.unlockShared(); |
| | | } |
| | | |
| | | private void releaseHeartBeatLock() { |
| | | sync.unlockExclusively(); |
| | | flushPendingBindOrStartTLSRequests(); |
| | | } |
| | | |
| | | /** |
| | | * Sends a heart beat on this connection if required to do so. |
| | | * |
| | | * @return {@code true} if a heart beat was sent, otherwise {@code false}. |
| | | */ |
| | | private boolean sendHeartBeat() { |
| | | /* |
| | | * Don't attempt to send a heart beat if the connection has already |
| | | * failed. |
| | | */ |
| | | if (!state.isValid()) { |
| | | return false; |
| | | } |
| | | |
| | | /* |
| | | * Only send the heart beat if the connection has been idle for some |
| | | * time. |
| | | */ |
| | | final long currentTimeMillis = timeService.now(); |
| | | if (currentTimeMillis < (lastResponseTimestamp + minDelayMS)) { |
| | | return false; |
| | | } |
| | | |
| | | /* |
| | | * Don't send a heart beat if there is already a heart beat, bind, |
| | | * or startTLS in progress. Note that the bind/startTLS response |
| | | * will update the lastResponseTimestamp as if it were a heart beat. |
| | | */ |
| | | if (sync.tryLockExclusively()) { |
| | | try { |
| | | connection.searchAsync(heartBeatRequest, new SearchResultHandler() { |
| | | @Override |
| | | public boolean handleEntry(final SearchResultEntry entry) { |
| | | timestamp(entry); |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | public boolean handleReference(final SearchResultReference reference) { |
| | | timestamp(reference); |
| | | return true; |
| | | } |
| | | }).onSuccess(new SuccessHandler<Result>() { |
| | | @Override |
| | | public void handleResult(Result result) { |
| | | timestamp(result); |
| | | releaseHeartBeatLock(); |
| | | } |
| | | }).onFailure(new FailureHandler<LdapException>() { |
| | | @Override |
| | | public void handleError(LdapException error) { |
| | | /* |
| | | * Connection failure will be handled by connection |
| | | * event listener. Ignore cancellation errors since |
| | | * these indicate that the heart beat was aborted by |
| | | * a client-side close. |
| | | */ |
| | | if (!(error instanceof CancelledResultException)) { |
| | | /* |
| | | * Log at debug level to avoid polluting the |
| | | * logs with benign password policy related |
| | | * errors. See OPENDJ-1168 and OPENDJ-1167. |
| | | */ |
| | | logger.debug(LocalizableMessage.raw("Heartbeat failed for connection factory '%s'", |
| | | factory, error)); |
| | | timestamp(error); |
| | | } |
| | | releaseHeartBeatLock(); |
| | | } |
| | | }); |
| | | } catch (final IllegalStateException e) { |
| | | /* |
| | | * This may happen when we attempt to send the heart beat |
| | | * just after the connection is closed but before we are |
| | | * notified. |
| | | */ |
| | | |
| | | /* |
| | | * Release the lock because we're never going to get a |
| | | * response. |
| | | */ |
| | | releaseHeartBeatLock(); |
| | | } |
| | | } |
| | | /* |
| | | * Indicate that a the heartbeat should be checked even if a |
| | | * bind/startTLS is in progress, since these operations will |
| | | * effectively act as the heartbeat. |
| | | */ |
| | | return true; |
| | | } |
| | | |
| | | private <R> R timestamp(final R response) { |
| | | if (!(response instanceof ConnectionException)) { |
| | | lastResponseTimestamp = timeService.now(); |
| | | } |
| | | return response; |
| | | } |
| | | |
| | | private <R extends Result> LdapPromise<R> enqueueBindOrStartTLSPromise( |
| | | AsyncFunction<Void, R, LdapException> doRequest) { |
| | | /* |
| | | * A heart beat must be in progress so create a runnable task which |
| | | * will be executed when the heart beat completes. |
| | | */ |
| | | final LdapPromiseImpl<Void> promise = newLdapPromiseImpl(); |
| | | final LdapPromise<R> result = promise.thenAsync(doRequest); |
| | | |
| | | // Enqueue and flush if the heart beat has completed in the mean |
| | | // time. |
| | | pendingBindOrStartTLSRequests.offer(new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | // FIXME: Handle cancel chaining. |
| | | if (!result.isCancelled()) { |
| | | sync.lockShared(); // Will not block. |
| | | promise.handleResult((Void) null); |
| | | } |
| | | } |
| | | }); |
| | | flushPendingBindOrStartTLSRequests(); |
| | | return result; |
| | | } |
| | | |
| | | private <R extends Result> LdapPromise<R> timestampPromise(LdapPromise<R> wrappedPromise) { |
| | | final LdapPromiseImpl<R> outerPromise = new LdapPromiseImplWrapper<R>(wrappedPromise); |
| | | pendingResults.add(outerPromise); |
| | | wrappedPromise.onSuccess(new SuccessHandler<R>() { |
| | | @Override |
| | | public void handleResult(R result) { |
| | | outerPromise.handleResult(result); |
| | | timestamp(result); |
| | | } |
| | | }).onFailure(new FailureHandler<LdapException>() { |
| | | @Override |
| | | public void handleError(LdapException error) { |
| | | outerPromise.handleError(error); |
| | | timestamp(error); |
| | | } |
| | | }); |
| | | outerPromise.onSuccessOrFailure(new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | pendingResults.remove(outerPromise); |
| | | } |
| | | }); |
| | | if (!checkState()) { |
| | | outerPromise.handleError(state.getConnectionError()); |
| | | } |
| | | return outerPromise; |
| | | } |
| | | |
| | | private <R extends Result> LdapPromise<R> timestampBindOrStartTLSPromise(LdapPromise<R> wrappedPromise) { |
| | | return timestampPromise(wrappedPromise).onSuccessOrFailure(new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | releaseBindOrStartTLSLock(); |
| | | } |
| | | }); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * This synchronizer prevents Bind or StartTLS operations from being |
| | | * processed concurrently with heart-beats. This is required because the |
| | | * LDAP protocol specifically states that servers receiving a Bind operation |
| | | * should either wait for existing operations to complete or abandon them. |
| | | * The same presumably applies to StartTLS operations. Note that concurrent |
| | | * bind/StartTLS operations are not permitted. |
| | | * <p> |
| | | * This connection factory only coordinates Bind and StartTLS requests with |
| | | * heart-beats. It does not attempt to prevent or control attempts to send |
| | | * multiple concurrent Bind or StartTLS operations, etc. |
| | | * <p> |
| | | * This synchronizer can be thought of as cross between a read-write lock |
| | | * and a semaphore. Unlike a read-write lock there is no requirement that a |
| | | * thread releasing a lock must hold it. In addition, this synchronizer does |
| | | * not support reentrancy. A thread attempting to acquire exclusively more |
| | | * than once will deadlock, and a thread attempting to acquire shared more |
| | | * than once will succeed and be required to release an equivalent number of |
| | | * times. |
| | | * <p> |
| | | * The synchronizer has three states: |
| | | * <ul> |
| | | * <li>UNLOCKED(0) - the synchronizer may be acquired shared or exclusively |
| | | * <li>LOCKED_EXCLUSIVELY(-1) - the synchronizer is held exclusively and |
| | | * cannot be acquired shared or exclusively. An exclusive lock is held while |
| | | * a heart beat is in progress |
| | | * <li>LOCKED_SHARED(>0) - the synchronizer is held shared and cannot be |
| | | * acquired exclusively. N shared locks are held while N Bind or StartTLS |
| | | * operations are in progress. |
| | | * </ul> |
| | | */ |
| | | private static final class Sync extends AbstractQueuedSynchronizer { |
| | | private static final int LOCKED_EXCLUSIVELY = -1; |
| | | /** Keep compiler quiet. */ |
| | | private static final long serialVersionUID = -3590428415442668336L; |
| | | |
| | | /** Lock states. Positive values indicate that the shared lock is taken. */ |
| | | private static final int UNLOCKED = 0; // initial state |
| | | |
| | | @Override |
| | | protected boolean isHeldExclusively() { |
| | | return getState() == LOCKED_EXCLUSIVELY; |
| | | } |
| | | |
| | | boolean isHeld() { |
| | | return getState() != 0; |
| | | } |
| | | |
| | | @Override |
| | | protected boolean tryAcquire(final int ignored) { |
| | | if (compareAndSetState(UNLOCKED, LOCKED_EXCLUSIVELY)) { |
| | | setExclusiveOwnerThread(Thread.currentThread()); |
| | | return true; |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | @Override |
| | | protected int tryAcquireShared(final int readers) { |
| | | for (;;) { |
| | | final int state = getState(); |
| | | if (state == LOCKED_EXCLUSIVELY) { |
| | | return LOCKED_EXCLUSIVELY; // failed |
| | | } |
| | | final int newState = state + readers; |
| | | if (compareAndSetState(state, newState)) { |
| | | return newState; // succeeded + more readers allowed |
| | | } |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | protected boolean tryRelease(final int ignored) { |
| | | if (getState() != LOCKED_EXCLUSIVELY) { |
| | | throw new IllegalMonitorStateException(); |
| | | } |
| | | setExclusiveOwnerThread(null); |
| | | setState(UNLOCKED); |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | protected boolean tryReleaseShared(final int ignored) { |
| | | for (;;) { |
| | | final int state = getState(); |
| | | if (state == UNLOCKED || state == LOCKED_EXCLUSIVELY) { |
| | | throw new IllegalMonitorStateException(); |
| | | } |
| | | final int newState = state - 1; |
| | | if (compareAndSetState(state, newState)) { |
| | | /* |
| | | * We could always return true here, but since there cannot |
| | | * be waiting readers we can specialize for waiting writers. |
| | | */ |
| | | return newState == UNLOCKED; |
| | | } |
| | | } |
| | | } |
| | | |
| | | void lockShared() { |
| | | acquireShared(1); |
| | | } |
| | | |
| | | boolean tryLockExclusively() { |
| | | return tryAcquire(0 /* unused */); |
| | | } |
| | | |
| | | boolean tryLockShared() { |
| | | return tryAcquireShared(1) > 0; |
| | | } |
| | | |
| | | void unlockExclusively() { |
| | | release(0 /* unused */); |
| | | } |
| | | |
| | | void unlockShared() { |
| | | releaseShared(0 /* unused */); |
| | | } |
| | | |
| | | } |
| | | |
| | | private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); |
| | | |
| | | /** |
| | | * Default heart beat which will target the root DSE but not return any |
| | | * results. |
| | | */ |
| | | private static final SearchRequest DEFAULT_SEARCH = Requests.newSearchRequest("", |
| | | SearchScope.BASE_OBJECT, "(objectClass=*)", "1.1"); |
| | | |
| | | /** |
| | | * This is package private in order to allow unit tests to inject fake time |
| | | * stamps. |
| | | */ |
| | | TimeService timeService = TimeService.SYSTEM; |
| | | |
| | | /** |
| | | * Scheduled task which checks that all heart beats have been received |
| | | * within the timeout period. |
| | | */ |
| | | private final Runnable checkHeartBeatRunnable = new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | for (final ConnectionImpl connection : getValidConnections()) { |
| | | connection.checkForHeartBeat(); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | /** |
| | | * Underlying connection factory. |
| | | */ |
| | | private final ConnectionFactory factory; |
| | | |
| | | /** |
| | | * The heartbeat scheduled future - which may be null if heartbeats are not |
| | | * being sent (no valid connections). |
| | | */ |
| | | private ScheduledFuture<?> heartBeatFuture; |
| | | |
| | | /** |
| | | * The heartbeat search request. |
| | | */ |
| | | private final SearchRequest heartBeatRequest; |
| | | |
| | | /** |
| | | * The interval between successive heartbeats. |
| | | */ |
| | | private final long interval; |
| | | private final TimeUnit intervalUnit; |
| | | |
| | | /** |
| | | * Flag which indicates whether this factory has been closed. |
| | | */ |
| | | private final AtomicBoolean isClosed = new AtomicBoolean(); |
| | | |
| | | /** |
| | | * The minimum amount of time the connection should remain idle (no |
| | | * responses) before starting to send heartbeats. |
| | | */ |
| | | private final long minDelayMS; |
| | | |
| | | /** |
| | | * Prevents the scheduler being released when there are remaining references |
| | | * (this factory or any connections). It is initially set to 1 because this |
| | | * factory has a reference. |
| | | */ |
| | | private final AtomicInteger referenceCount = new AtomicInteger(1); |
| | | |
| | | /** |
| | | * The heartbeat scheduler. |
| | | */ |
| | | private final ReferenceCountedObject<ScheduledExecutorService>.Reference scheduler; |
| | | |
| | | |
| | | /** |
| | | * Scheduled task which sends heart beats for all valid idle connections. |
| | | */ |
| | | private final Runnable sendHeartBeatRunnable = new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | boolean heartBeatSent = false; |
| | | for (final ConnectionImpl connection : getValidConnections()) { |
| | | heartBeatSent |= connection.sendHeartBeat(); |
| | | } |
| | | if (heartBeatSent) { |
| | | scheduler.get().schedule(checkHeartBeatRunnable, timeoutMS, TimeUnit.MILLISECONDS); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | /** |
| | | * The heartbeat timeout in milli-seconds. The connection will be marked as |
| | | * failed if no heartbeat response is received within the timeout. |
| | | */ |
| | | private final long timeoutMS; |
| | | |
| | | /** |
| | | * List of valid connections to which heartbeats will be sent. |
| | | */ |
| | | private final List<ConnectionImpl> validConnections = new LinkedList<ConnectionImpl>(); |
| | | |
| | | HeartBeatConnectionFactory(final ConnectionFactory factory, final long interval, |
| | | final long timeout, final TimeUnit unit, final SearchRequest heartBeat, |
| | | final ScheduledExecutorService scheduler) { |
| | | Reject.ifNull(factory, unit); |
| | | Reject.ifFalse(interval >= 0, "negative interval"); |
| | | Reject.ifFalse(timeout >= 0, "negative timeout"); |
| | | |
| | | this.heartBeatRequest = heartBeat != null ? heartBeat : DEFAULT_SEARCH; |
| | | this.interval = interval; |
| | | this.intervalUnit = unit; |
| | | this.factory = factory; |
| | | this.scheduler = DEFAULT_SCHEDULER.acquireIfNull(scheduler); |
| | | this.timeoutMS = unit.toMillis(timeout); |
| | | this.minDelayMS = unit.toMillis(interval) / 2; |
| | | } |
| | | |
| | | @Override |
| | | public void close() { |
| | | if (isClosed.compareAndSet(false, true)) { |
| | | synchronized (validConnections) { |
| | | if (!validConnections.isEmpty()) { |
| | | logger.debug(LocalizableMessage.raw( |
| | | "HeartbeatConnectionFactory '%s' is closing while %d active connections remain", |
| | | this, validConnections.size())); |
| | | } |
| | | } |
| | | releaseScheduler(); |
| | | factory.close(); |
| | | } |
| | | } |
| | | |
| | | private void releaseScheduler() { |
| | | if (referenceCount.decrementAndGet() == 0) { |
| | | scheduler.release(); |
| | | } |
| | | } |
| | | |
| | | private void acquireScheduler() { |
| | | /* |
| | | * If the factory is not closed then we need to prevent the scheduler |
| | | * from being released while the connection attempt is in progress. |
| | | */ |
| | | referenceCount.incrementAndGet(); |
| | | if (isClosed.get()) { |
| | | releaseScheduler(); |
| | | throw new IllegalStateException("Attempted to get a connection after factory close"); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public Connection getConnection() throws LdapException { |
| | | /* |
| | | * Immediately send a heart beat in order to determine if the connected |
| | | * server is responding. |
| | | */ |
| | | acquireScheduler(); // Protect scheduler. |
| | | Connection connection = null; |
| | | try { |
| | | connection = factory.getConnection(); |
| | | connection.searchAsync(heartBeatRequest, null).getOrThrow(timeoutMS, TimeUnit.MILLISECONDS); |
| | | return registerConnection(new ConnectionImpl(connection)); |
| | | } catch (final Exception e) { |
| | | closeSilently(connection); |
| | | releaseScheduler(); |
| | | throw adaptHeartBeatError(e); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public Promise<Connection, LdapException> getConnectionAsync() { |
| | | acquireScheduler(); // Protect scheduler. |
| | | |
| | | final AtomicReference<Connection> connectionHolder = new AtomicReference<Connection>(); |
| | | final PromiseImpl<Connection, LdapException> promise = PromiseImpl.create(); |
| | | |
| | | // Request a connection and return the promise representing the heartbeat. |
| | | factory.getConnectionAsync().thenAsync(new AsyncFunction<Connection, Result, LdapException>() { |
| | | @Override |
| | | public Promise<Result, LdapException> apply(final Connection connection) { |
| | | // Save the connection for later once the heart beat completes. |
| | | connectionHolder.set(connection); |
| | | scheduler.get().schedule(new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | if (promise.tryHandleError(newHeartBeatTimeoutError())) { |
| | | closeSilently(connectionHolder.get()); |
| | | releaseScheduler(); |
| | | } |
| | | } |
| | | }, timeoutMS, TimeUnit.MILLISECONDS); |
| | | return connection.searchAsync(heartBeatRequest, null); |
| | | } |
| | | }).onSuccess(new SuccessHandler<Result>() { |
| | | @Override |
| | | public void handleResult(Result result) { |
| | | final Connection connection = connectionHolder.get(); |
| | | final ConnectionImpl connectionImpl = new ConnectionImpl(connection); |
| | | if (!promise.tryHandleResult(registerConnection(connectionImpl))) { |
| | | connectionImpl.close(); |
| | | } |
| | | } |
| | | }).onFailure(new FailureHandler<LdapException>() { |
| | | @Override |
| | | public void handleError(LdapException error) { |
| | | if (promise.tryHandleError(adaptHeartBeatError(error))) { |
| | | closeSilently(connectionHolder.get()); |
| | | releaseScheduler(); |
| | | } |
| | | } |
| | | }); |
| | | |
| | | return promise; |
| | | } |
| | | |
| | | @Override |
| | | public String toString() { |
| | | final StringBuilder builder = new StringBuilder(); |
| | | builder.append("HeartBeatConnectionFactory("); |
| | | builder.append(factory); |
| | | builder.append(')'); |
| | | return builder.toString(); |
| | | } |
| | | |
| | | private Connection registerConnection(final ConnectionImpl heartBeatConnection) { |
| | | synchronized (validConnections) { |
| | | if (validConnections.isEmpty()) { |
| | | /* This is the first active connection, so start the heart beat. */ |
| | | heartBeatFuture = |
| | | scheduler.get().scheduleWithFixedDelay(sendHeartBeatRunnable, 0, interval, intervalUnit); |
| | | } |
| | | validConnections.add(heartBeatConnection); |
| | | return heartBeatConnection; |
| | | } |
| | | } |
| | | |
| | | private LdapException adaptHeartBeatError(final Exception error) { |
| | | if (error instanceof ConnectionException) { |
| | | return (LdapException) error; |
| | | } else if (error instanceof TimeoutResultException || error instanceof TimeoutException) { |
| | | return newHeartBeatTimeoutError(); |
| | | } else if (error instanceof InterruptedException) { |
| | | return newLdapException(ResultCode.CLIENT_SIDE_USER_CANCELLED, error); |
| | | } else { |
| | | return newLdapException(ResultCode.CLIENT_SIDE_SERVER_DOWN, HBCF_HEARTBEAT_FAILED.get(), error); |
| | | } |
| | | } |
| | | |
| | | private ConnectionImpl[] getValidConnections() { |
| | | final ConnectionImpl[] tmp; |
| | | synchronized (validConnections) { |
| | | tmp = validConnections.toArray(new ConnectionImpl[0]); |
| | | } |
| | | return tmp; |
| | | } |
| | | |
| | | private LdapException newHeartBeatTimeoutError() { |
| | | return newLdapException(ResultCode.CLIENT_SIDE_SERVER_DOWN, HBCF_HEARTBEAT_TIMEOUT |
| | | .get(timeoutMS)); |
| | | } |
| | | } |
| | |
| | | * The host name. |
| | | * @param port |
| | | * The port number. |
| | | * @throws NullPointerException |
| | | * If {@code host} was {@code null}. |
| | | * @throws ProviderNotFoundException if no provider is available or if the |
| | | * provider requested using options is not found. |
| | | */ |
| | | public LDAPConnectionFactory(final String host, final int port) { |
| | | this(host, port, new LDAPOptions()); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new LDAP connection factory which can be used to create LDAP |
| | | * connections to the Directory Server at the provided host and port |
| | | * number. |
| | | * |
| | | * @param host |
| | | * The host name. |
| | | * @param port |
| | | * The port number. |
| | | * @param options |
| | | * The LDAP options to use when creating connections. |
| | | * @throws NullPointerException |
| | |
| | | * @throws ProviderNotFoundException if no provider is available or if the |
| | | * provider requested using options is not found. |
| | | */ |
| | | LDAPConnectionFactory(final String host, final int port, final LDAPOptions options) { |
| | | public LDAPConnectionFactory(final String host, final int port, final LDAPOptions options) { |
| | | Reject.ifNull(host, options); |
| | | this.provider = getProvider(TransportProvider.class, options.getTransportProvider(), |
| | | options.getProviderClassLoader()); |
| | |
| | | /** |
| | | * Transport provider that provides the implementation of this listener. |
| | | */ |
| | | private final TransportProvider provider; |
| | | private TransportProvider provider; |
| | | |
| | | /** |
| | | * Creates a new LDAP listener implementation which will listen for LDAP |
| | | * client connections at the provided address. |
| | | * |
| | | * @param port |
| | | * The port to listen on. |
| | | * @param factory |
| | | * The server connection factory which will be used to create |
| | | * server connections. |
| | | * @throws IOException |
| | | * If an error occurred while trying to listen on the provided |
| | | * address. |
| | | * @throws NullPointerException |
| | | * If {code factory} was {@code null}. |
| | | */ |
| | | public LDAPListener(final int port, |
| | | final ServerConnectionFactory<LDAPClientContext, Integer> factory) throws IOException { |
| | | this(port, factory, new LDAPListenerOptions()); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new LDAP listener implementation which will listen for LDAP |
| | | * client connections at the provided address. |
| | | * |
| | | * @param port |
| | | * The port to listen on. |
| | | * @param factory |
| | | * The server connection factory which will be used to create |
| | | * server connections. |
| | | * @param options |
| | | * The LDAP listener options. |
| | | * @throws IOException |
| | | * If an error occurred while trying to listen on the provided |
| | | * address. |
| | | * @throws NullPointerException |
| | | * If {code factory} or {@code options} was {@code null}. |
| | | */ |
| | | public LDAPListener(final int port, |
| | | final ServerConnectionFactory<LDAPClientContext, Integer> factory, |
| | | final LDAPListenerOptions options) throws IOException { |
| | | Reject.ifNull(factory, options); |
| | | final InetSocketAddress address = new InetSocketAddress(port); |
| | | this.provider = getProvider(TransportProvider.class, options.getTransportProvider(), |
| | | options.getProviderClassLoader()); |
| | | this.impl = provider.getLDAPListener(address, factory, options); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new LDAP listener implementation which will listen for LDAP |
| | | * client connections at the provided address. |
| | | * |
| | | * @param address |
| | | * The address to listen on. |
| | | * @param factory |
| | | * The server connection factory which will be used to create |
| | | * server connections. |
| | | * @throws IOException |
| | | * If an error occurred while trying to listen on the provided |
| | | * address. |
| | | * @throws NullPointerException |
| | | * If {@code address} or {code factory} was {@code null}. |
| | | */ |
| | | public LDAPListener(final InetSocketAddress address, |
| | | final ServerConnectionFactory<LDAPClientContext, Integer> factory) throws IOException { |
| | | this(address, factory, new LDAPListenerOptions()); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new LDAP listener implementation which will listen for LDAP |
| | |
| | | * If {@code address}, {code factory}, or {@code options} was |
| | | * {@code null}. |
| | | */ |
| | | LDAPListener(final InetSocketAddress address, |
| | | public LDAPListener(final InetSocketAddress address, |
| | | final ServerConnectionFactory<LDAPClientContext, Integer> factory, |
| | | final LDAPListenerOptions options) throws IOException { |
| | | Reject.ifNull(address, factory, options); |
| | |
| | | } |
| | | |
| | | /** |
| | | * Creates a new LDAP listener implementation which will listen for LDAP |
| | | * client connections at the provided address. |
| | | * |
| | | * @param host |
| | | * The address to listen on. |
| | | * @param port |
| | | * The port to listen on. |
| | | * @param factory |
| | | * The server connection factory which will be used to create |
| | | * server connections. |
| | | * @throws IOException |
| | | * If an error occurred while trying to listen on the provided |
| | | * address. |
| | | * @throws NullPointerException |
| | | * If {@code host} or {code factory} was {@code null}. |
| | | */ |
| | | public LDAPListener(final String host, final int port, |
| | | final ServerConnectionFactory<LDAPClientContext, Integer> factory) throws IOException { |
| | | this(host, port, factory, new LDAPListenerOptions()); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new LDAP listener implementation which will listen for LDAP |
| | | * client connections at the provided address. |
| | | * |
| | | * @param host |
| | | * The address to listen on. |
| | | * @param port |
| | | * The port to listen on. |
| | | * @param factory |
| | | * The server connection factory which will be used to create |
| | | * server connections. |
| | | * @param options |
| | | * The LDAP listener options. |
| | | * @throws IOException |
| | | * If an error occurred while trying to listen on the provided |
| | | * address. |
| | | * @throws NullPointerException |
| | | * If {@code host}, {code factory}, or {@code options} was |
| | | * {@code null}. |
| | | */ |
| | | public LDAPListener(final String host, final int port, |
| | | final ServerConnectionFactory<LDAPClientContext, Integer> factory, |
| | | final LDAPListenerOptions options) throws IOException { |
| | | Reject.ifNull(host, factory, options); |
| | | final InetSocketAddress address = new InetSocketAddress(host, port); |
| | | this.provider = getProvider(TransportProvider.class, options.getTransportProvider(), |
| | | options.getProviderClassLoader()); |
| | | this.impl = provider.getLDAPListener(address, factory, options); |
| | | } |
| | | |
| | | /** |
| | | * Closes this LDAP connection listener. |
| | | */ |
| | | @Override |
| | |
| | | * |
| | | * |
| | | * Copyright 2010 Sun Microsystems, Inc. |
| | | * Portions copyright 2012-2014 ForgeRock AS. |
| | | * Portions copyright 2012-2013 ForgeRock AS. |
| | | */ |
| | | |
| | | package org.forgerock.opendj.ldap; |
| | | |
| | | import java.util.LinkedList; |
| | | import java.util.List; |
| | | import java.util.concurrent.ScheduledExecutorService; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | import javax.net.ssl.SSLContext; |
| | | |
| | | import org.forgerock.opendj.ldap.requests.BindRequest; |
| | | import org.forgerock.opendj.ldap.requests.SearchRequest; |
| | | import org.forgerock.util.Reject; |
| | | |
| | | import static java.util.concurrent.TimeUnit.*; |
| | | |
| | | import static org.forgerock.opendj.ldap.requests.Requests.*; |
| | | |
| | | /** |
| | | * Common options for LDAP client connections. |
| | | * <p> |
| | |
| | | DEFAULT_CONNECT_TIMEOUT = getIntProperty("org.forgerock.opendj.io.connectTimeout", 5000); |
| | | } |
| | | |
| | | /** Default heart beat search request. */ |
| | | private static final SearchRequest DEFAULT_HEART_BEAT_SEARCH_REQUEST = |
| | | unmodifiableSearchRequest(newSearchRequest("", SearchScope.BASE_OBJECT, "(objectClass=*)", "1.1")); |
| | | |
| | | /** The heartbeat search request. Default request will target the root DSE but not return any results. */ |
| | | private SearchRequest heartBeatSearchRequest = DEFAULT_HEART_BEAT_SEARCH_REQUEST; |
| | | |
| | | /** The heartbeat bind request, if any. Default value is {@code null}. */ |
| | | private BindRequest bindRequest; |
| | | |
| | | /** The interval between successive heartbeats (milliseconds). Default value is 0 (means heartbeat disabled). */ |
| | | private long heartBeatIntervalInMillis = 0; |
| | | |
| | | /** |
| | | * The heartbeat scheduler. |
| | | * Default value is {@code null}. |
| | | * If no scheduler is provided while creating new {@link HeartBeatConnectionFactory}, |
| | | * the factory will use his default one. |
| | | */ |
| | | private ScheduledExecutorService heartBeatScheduler; |
| | | |
| | | /** General Options. */ |
| | | private SSLContext sslContext; |
| | | private boolean useStartTLS; |
| | | private long timeoutInMillis = DEFAULT_TIMEOUT; |
| | |
| | | this.enabledCipherSuites.addAll(options.getEnabledCipherSuites()); |
| | | this.enabledProtocols.addAll(options.getEnabledProtocols()); |
| | | this.connectTimeoutInMillis = options.connectTimeoutInMillis; |
| | | this.bindRequest = options.bindRequest; |
| | | this.heartBeatIntervalInMillis = options.heartBeatIntervalInMillis; |
| | | this.heartBeatSearchRequest = options.heartBeatSearchRequest; |
| | | this.heartBeatScheduler = options.heartBeatScheduler; |
| | | } |
| | | |
| | | /** |
| | |
| | | } |
| | | |
| | | /** |
| | | * Returns the bind request which will be sent as soon as the connection |
| | | * attempt succeeds, or null if unauthenticated connections should be |
| | | * returned. |
| | | * <p> |
| | | * By default, the bind request is {@code null}. |
| | | * |
| | | * @return The bind request which will be sent as soon as the connection |
| | | * attempt succeeds, or null if unauthenticated connections should |
| | | * be returned. |
| | | */ |
| | | public BindRequest getBindRequest() { |
| | | return bindRequest; |
| | | } |
| | | |
| | | /** |
| | | * 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 |
| | |
| | | * timeout. |
| | | */ |
| | | public long getConnectTimeout(final TimeUnit unit) { |
| | | return unit.convert(connectTimeoutInMillis, MILLISECONDS); |
| | | return unit.convert(connectTimeoutInMillis, TimeUnit.MILLISECONDS); |
| | | } |
| | | |
| | | /** |
| | |
| | | } |
| | | |
| | | /** |
| | | * Returns the interval between successive heart beat in the specified unit. |
| | | * <p> |
| | | * By default, heart beat interval value is 0, indicating that heart beat |
| | | * support will be disabled in the {@link LDAPConnectionFactory}. |
| | | * |
| | | * @param unit |
| | | * The time unit. |
| | | * @return The interval between keepalive pings in the specified time unit. |
| | | */ |
| | | public long getHeartBeatInterval(final TimeUnit unit) { |
| | | return unit.convert(heartBeatIntervalInMillis, MILLISECONDS); |
| | | } |
| | | |
| | | /** |
| | | * Returns the scheduler which should periodically send keepalive pings. |
| | | * <p> |
| | | * By default, the scheduler is {@code null}, indicating that the associated |
| | | * {@link LDAPConnectionFactory} will use the default one. |
| | | * |
| | | * @return Returns the {@link ScheduledExecutorService} instance which |
| | | * should periodically send keepalive pings. |
| | | */ |
| | | public ScheduledExecutorService getHeartBeatScheduler() { |
| | | return heartBeatScheduler; |
| | | } |
| | | |
| | | /** |
| | | * Returns the search request to use for keepalive pings. |
| | | * <p> |
| | | * The default {@link SearchRequest} will target the root DSE but not return |
| | | * any results. |
| | | * |
| | | * @return the {@link SearchRequest} to use for keepalive pings. |
| | | */ |
| | | public SearchRequest getHeartBeatSearchRequest() { |
| | | return heartBeatSearchRequest; |
| | | } |
| | | |
| | | /** |
| | | * Returns the SSL context which will be used when initiating connections |
| | | * with the Directory Server. |
| | | * <p> |
| | |
| | | * timeout. |
| | | */ |
| | | public long getTimeout(final TimeUnit unit) { |
| | | return unit.convert(timeoutInMillis, MILLISECONDS); |
| | | } |
| | | |
| | | /** |
| | | * Sets the bind request which will be sent as soon as the |
| | | * connection attempt succeeds. |
| | | * <p> |
| | | * By default, the bind request is {@code null}, indicating that |
| | | * unauthenticated connections should be returned by the associated |
| | | * {@link LDAPConnectionFactory} |
| | | * |
| | | * @param bindRequest |
| | | * The bind request which will be sent as soon as the connection |
| | | * attempt succeeds. |
| | | * @return A reference to this set of options. |
| | | */ |
| | | public LDAPOptions setBindRequest(final BindRequest bindRequest) { |
| | | this.bindRequest = bindRequest; |
| | | return this; |
| | | return unit.convert(timeoutInMillis, TimeUnit.MILLISECONDS); |
| | | } |
| | | |
| | | /** |
| | |
| | | } |
| | | |
| | | /** |
| | | * Sets the interval between successive heartbeats in the specified unit. |
| | | * <p> |
| | | * By default, heart beat interval value is 0, indicating that heart beat |
| | | * will be disabled in the {@link LDAPConnectionFactory}. |
| | | * |
| | | * @param interval |
| | | * The interval between keepalive pings in the specified time unit. |
| | | * @param unit |
| | | * The time unit of the given interval. |
| | | * @return A reference to this set of options. |
| | | */ |
| | | public LDAPOptions setHeartBeatInterval(final long interval, final TimeUnit unit) { |
| | | Reject.ifTrue(interval < 0, "negative timeout"); |
| | | this.heartBeatIntervalInMillis = MILLISECONDS.convert(interval, unit); |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * The scheduler which should for periodically sending keepalive pings. |
| | | * <p> |
| | | * By default, the scheduler is {@code null}, indicating that the associated |
| | | * {@link LDAPConnectionFactory} will use the default one. |
| | | * |
| | | * @param scheduler |
| | | * The scheduler which should for periodically sending keepalive |
| | | * pings. |
| | | * @return A reference to this set of options. |
| | | */ |
| | | public LDAPOptions setHeartBeatScheduler(final ScheduledExecutorService scheduler) { |
| | | this.heartBeatScheduler = scheduler; |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * Sets the search request to use for keepalive pings. |
| | | * <p> |
| | | * The default {@link SearchRequest} will target the root DSE but not return |
| | | * any results. |
| | | * |
| | | * @param heartBeatRequest |
| | | * The search request to use for keepalive pings. |
| | | * @return A reference to this set of options. |
| | | */ |
| | | public LDAPOptions setHeartBeatSearchRequest(final SearchRequest heartBeatRequest) { |
| | | this.heartBeatSearchRequest = heartBeatRequest; |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * Sets the SSL context which will be used when initiating connections with |
| | | * the Directory Server. |
| | | * <p> |
| | |
| | | WARN_ATTR_INVALID_PARTIAL_TIME_ASSERTION_FORMAT=The provided \ |
| | | value "%s" could not be parsed as a valid assertion value because the \ |
| | | character '%c' is not allowed. The acceptable values are s (second), \ |
| | | m (minute), h (hour), D (date), M (month) and Y (year) |
| | | # |
| | | # Connection Timeout Messages |
| | | # |
| | | LDAP_CONNECTION_REQUEST_TIMEOUT=The request has failed because no response \ |
| | | was received from the server within the %d ms timeout |
| | | 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 |
| | | LDAP_CONNECTION_BIND_OR_START_TLS_CONNECTION_TIMEOUT=The LDAP connection has \ |
| | | failed because no bind or StartTLS response was received from the server \ |
| | | within the %d ms timeout |
| | | |
| | | m (minute), h (hour), D (date), M (month) and Y (year) |
| New file |
| | |
| | | /* |
| | | * 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 2013-2014 ForgeRock AS. |
| | | */ |
| | | |
| | | package org.forgerock.opendj.ldap; |
| | | |
| | | import java.util.LinkedList; |
| | | import java.util.List; |
| | | import java.util.concurrent.TimeUnit; |
| | | import java.util.logging.Level; |
| | | |
| | | import org.forgerock.opendj.ldap.requests.BindRequest; |
| | | import org.forgerock.opendj.ldap.requests.SearchRequest; |
| | | import org.forgerock.opendj.ldap.responses.BindResult; |
| | | import org.forgerock.opendj.ldap.responses.Result; |
| | | import org.forgerock.opendj.ldap.spi.BindResultLdapPromiseImpl; |
| | | import org.forgerock.util.promise.FailureHandler; |
| | | import org.forgerock.util.promise.NeverThrowsException; |
| | | import org.forgerock.util.promise.Promise; |
| | | import org.forgerock.util.promise.PromiseImpl; |
| | | import org.forgerock.util.promise.SuccessHandler; |
| | | import org.mockito.ArgumentCaptor; |
| | | import org.mockito.invocation.InvocationOnMock; |
| | | import org.mockito.stubbing.Answer; |
| | | import org.testng.annotations.AfterClass; |
| | | import org.testng.annotations.AfterMethod; |
| | | import org.testng.annotations.BeforeClass; |
| | | import org.testng.annotations.Test; |
| | | |
| | | import static org.fest.assertions.Assertions.*; |
| | | import static org.fest.assertions.Fail.*; |
| | | import static org.forgerock.opendj.ldap.LdapException.*; |
| | | import static org.forgerock.opendj.ldap.ResultCode.*; |
| | | import static org.forgerock.opendj.ldap.SearchScope.*; |
| | | import static org.forgerock.opendj.ldap.TestCaseUtils.*; |
| | | import static org.forgerock.opendj.ldap.requests.Requests.*; |
| | | import static org.forgerock.opendj.ldap.responses.Responses.*; |
| | | import static org.forgerock.opendj.ldap.spi.LdapPromiseImpl.*; |
| | | import static org.forgerock.opendj.ldap.spi.LdapPromises.*; |
| | | import static org.mockito.Matchers.*; |
| | | import static org.mockito.Mockito.*; |
| | | |
| | | /** |
| | | * Tests the connection pool implementation.. |
| | | */ |
| | | @SuppressWarnings("javadoc") |
| | | public class HeartBeatConnectionFactoryTestCase extends SdkTestCase { |
| | | |
| | | // @formatter:off |
| | | |
| | | /* |
| | | * Key features which need testing: |
| | | * |
| | | * - lazy scheduler registration and deregistration |
| | | * - scheduled task only sends heart-beat when connection is open |
| | | * - connection remains valid when any response is received |
| | | * - connection remains valid when a heart beat response is received |
| | | * - connection becomes invalid if no response is received during timeout |
| | | * - heart beat only sent when connection is idle |
| | | * - slow bind / startTLS prevents heart beat |
| | | * - slow heart beat prevents bind / start TLS |
| | | * - support concurrent bind / start TLS |
| | | */ |
| | | |
| | | // @formatter:on |
| | | |
| | | private static final SearchRequest HEARTBEAT = newSearchRequest("dc=test", BASE_OBJECT, |
| | | "(objectclass=*)", "1.1"); |
| | | |
| | | private Connection connection; |
| | | private ConnectionFactory factory; |
| | | private Connection hbc; |
| | | private HeartBeatConnectionFactory hbcf; |
| | | private List<ConnectionEventListener> listeners; |
| | | private MockScheduler scheduler; |
| | | |
| | | /** |
| | | * Disables logging before the tests. |
| | | */ |
| | | @BeforeClass |
| | | public void disableLogging() { |
| | | TestCaseUtils.setDefaultLogLevel(Level.SEVERE); |
| | | } |
| | | |
| | | /** |
| | | * Re-enable logging after the tests. |
| | | */ |
| | | @AfterClass |
| | | public void enableLogging() { |
| | | TestCaseUtils.setDefaultLogLevel(Level.INFO); |
| | | } |
| | | |
| | | @AfterMethod(alwaysRun = true) |
| | | public void tearDown() { |
| | | try { |
| | | if (hbc != null) { |
| | | hbc.close(); |
| | | assertThat(hbc.isValid()).isFalse(); |
| | | assertThat(hbc.isClosed()).isTrue(); |
| | | } |
| | | scheduler.runAllTasks(); // Flush any remaining timeout tasks. |
| | | assertThat(scheduler.isScheduled()).isFalse(); // No more connections to check. |
| | | hbcf.close(); |
| | | } finally { |
| | | connection = null; |
| | | factory = null; |
| | | hbcf = null; |
| | | listeners = null; |
| | | scheduler = null; |
| | | hbc = null; |
| | | } |
| | | } |
| | | |
| | | @Test |
| | | public void testBindWhileHeartBeatInProgress() throws Exception { |
| | | mockConnectionWithInitialHeartbeatResult(ResultCode.SUCCESS); |
| | | mockBindAsyncResponse(); |
| | | hbc = hbcf.getConnection(); |
| | | |
| | | /* |
| | | * Send a heartbeat, trapping the search call-back so that we can send |
| | | * the response once we have attempted a bind. |
| | | */ |
| | | when(connection.searchAsync(any(SearchRequest.class), any(SearchResultHandler.class))).thenReturn( |
| | | newSuccessfulLdapPromise(newResult(SUCCESS))); |
| | | when(hbcf.timeService.now()).thenReturn(11000L); |
| | | scheduler.runAllTasks(); // Send the heartbeat. |
| | | |
| | | // Capture the heartbeat search result handler. |
| | | verify(connection, times(2)).searchAsync(same(HEARTBEAT), any(SearchResultHandler.class)); |
| | | assertThat(hbc.isValid()).isTrue(); // Not checked yet. |
| | | |
| | | /* |
| | | * Now attempt a bind request, which should be held in a queue until the |
| | | * heart beat completes. |
| | | */ |
| | | hbc.bindAsync(newSimpleBindRequest()); |
| | | verify(connection, times(0)).bindAsync(any(BindRequest.class)); |
| | | |
| | | // Send fake heartbeat response, releasing the bind request. |
| | | verify(connection, times(1)).bindAsync(any(BindRequest.class), any(IntermediateResponseHandler.class)); |
| | | } |
| | | |
| | | @Test |
| | | public void testGetConnection() throws Exception { |
| | | mockConnectionWithInitialHeartbeatResult(ResultCode.SUCCESS); |
| | | hbc = hbcf.getConnection(); |
| | | assertThat(hbc).isNotNull(); |
| | | assertThat(hbc.isValid()).isTrue(); |
| | | } |
| | | |
| | | @Test |
| | | public void testGetConnectionAsync() throws Exception { |
| | | @SuppressWarnings("unchecked") |
| | | final SuccessHandler<Connection> mockSuccessHandler = mock(SuccessHandler.class); |
| | | |
| | | mockConnectionWithInitialHeartbeatResult(ResultCode.SUCCESS); |
| | | hbc = hbcf.getConnectionAsync().onSuccess(mockSuccessHandler).getOrThrow(); |
| | | assertThat(hbc).isNotNull(); |
| | | assertThat(hbc.isValid()).isTrue(); |
| | | |
| | | verify(mockSuccessHandler).handleResult(any(Connection.class)); |
| | | verifyNoMoreInteractions(mockSuccessHandler); |
| | | } |
| | | |
| | | @Test |
| | | public void testGetConnectionAsyncWithInitialHeartBeatError() throws Exception { |
| | | @SuppressWarnings("unchecked") |
| | | final SuccessHandler<Connection> mockSuccessHandler = mock(SuccessHandler.class); |
| | | final PromiseImpl<LdapException, NeverThrowsException> promisedError = PromiseImpl.create(); |
| | | |
| | | mockConnectionWithInitialHeartbeatResult(ResultCode.BUSY); |
| | | Promise<? extends Connection, LdapException> promise = hbcf.getConnectionAsync(); |
| | | promise.onSuccess(mockSuccessHandler).onFailure(new FailureHandler<LdapException>() { |
| | | @Override |
| | | public void handleError(LdapException error) { |
| | | promisedError.handleResult(error); |
| | | } |
| | | }); |
| | | |
| | | checkInitialHeartBeatFailure(promisedError.getOrThrow()); |
| | | |
| | | try { |
| | | promise.getOrThrow(); |
| | | fail("Unexpectedly obtained a connection"); |
| | | } catch (final LdapException e) { |
| | | checkInitialHeartBeatFailure(e); |
| | | } |
| | | |
| | | verifyNoMoreInteractions(mockSuccessHandler); |
| | | } |
| | | |
| | | @Test |
| | | public void testGetConnectionWithInitialHeartBeatError() throws Exception { |
| | | mockConnectionWithInitialHeartbeatResult(ResultCode.BUSY); |
| | | try { |
| | | hbcf.getConnection(); |
| | | fail("Unexpectedly obtained a connection"); |
| | | } catch (final LdapException e) { |
| | | checkInitialHeartBeatFailure(e); |
| | | } |
| | | } |
| | | |
| | | @Test |
| | | public void testHeartBeatSucceedsThenFails() throws Exception { |
| | | mockConnectionWithInitialHeartbeatResult(ResultCode.SUCCESS); |
| | | |
| | | // Get a connection and check that it was pinged. |
| | | hbc = hbcf.getConnection(); |
| | | |
| | | verifyHeartBeatSent(connection, 1); |
| | | assertThat(scheduler.isScheduled()).isTrue(); // heartbeater |
| | | assertThat(listeners).hasSize(1); |
| | | assertThat(hbc.isValid()).isTrue(); |
| | | |
| | | // Invoke heartbeat before the connection is considered idle. |
| | | scheduler.runAllTasks(); |
| | | verifyHeartBeatSent(connection, 1); // No heartbeat sent - not idle yet. |
| | | assertThat(hbc.isValid()).isTrue(); |
| | | |
| | | // Invoke heartbeat after the connection is considered idle. |
| | | when(hbcf.timeService.now()).thenReturn(6000L); |
| | | scheduler.runAllTasks(); |
| | | verifyHeartBeatSent(connection, 2); // Heartbeat sent. |
| | | assertThat(hbc.isValid()).isTrue(); |
| | | |
| | | // Now force the heartbeat to fail. |
| | | mockHeartBeatResponse(connection, listeners, ResultCode.CLIENT_SIDE_SERVER_DOWN); |
| | | when(hbcf.timeService.now()).thenReturn(11000L); |
| | | scheduler.runAllTasks(); |
| | | verifyHeartBeatSent(connection, 3); |
| | | assertThat(hbc.isValid()).isFalse(); |
| | | |
| | | // Flush redundant timeout tasks. |
| | | scheduler.runAllTasks(); |
| | | |
| | | // Attempt to send a new request: it should fail immediately. |
| | | @SuppressWarnings("unchecked") |
| | | final FailureHandler<LdapException> mockHandler = mock(FailureHandler.class); |
| | | hbc.modifyAsync(newModifyRequest(DN.rootDN())).onFailure(mockHandler); |
| | | final ArgumentCaptor<LdapException> arg = ArgumentCaptor.forClass(LdapException.class); |
| | | verify(mockHandler).handleError(arg.capture()); |
| | | assertThat(arg.getValue().getResult().getResultCode()).isEqualTo( |
| | | ResultCode.CLIENT_SIDE_SERVER_DOWN); |
| | | |
| | | assertThat(hbc.isValid()).isFalse(); |
| | | assertThat(hbc.isClosed()).isFalse(); |
| | | } |
| | | |
| | | @Test |
| | | public void testHeartBeatTimeout() throws Exception { |
| | | mockConnectionWithInitialHeartbeatResult(ResultCode.SUCCESS); |
| | | |
| | | // Get a connection and check that it was pinged. |
| | | hbc = hbcf.getConnection(); |
| | | |
| | | // Now force the heartbeat to fail due to timeout. |
| | | mockHeartBeatResponse(connection, listeners, null /* no response */); |
| | | when(hbcf.timeService.now()).thenReturn(11000L); |
| | | scheduler.runAllTasks(); // Send the heartbeat. |
| | | verifyHeartBeatSent(connection, 2); |
| | | assertThat(hbc.isValid()).isTrue(); // Not checked yet. |
| | | when(hbcf.timeService.now()).thenReturn(12000L); |
| | | scheduler.runAllTasks(); // Check for heartbeat. |
| | | assertThat(hbc.isValid()).isFalse(); // Now invalid. |
| | | assertThat(hbc.isClosed()).isFalse(); |
| | | } |
| | | |
| | | @SuppressWarnings({ "rawtypes", "unchecked" }) |
| | | @Test(description = "OPENDJ-1348") |
| | | public void testBindPreventsHeartBeatTimeout() throws Exception { |
| | | mockConnectionWithInitialHeartbeatResult(ResultCode.SUCCESS); |
| | | BindResultLdapPromiseImpl promise = mockBindAsyncResponse(); |
| | | hbc = hbcf.getConnection(); |
| | | |
| | | /* |
| | | * Send a bind request, trapping the bind call-back so that we can send |
| | | * the response once we have attempted a heartbeat. |
| | | */ |
| | | when(hbcf.timeService.now()).thenReturn(11000L); |
| | | hbc.bindAsync(newSimpleBindRequest()); |
| | | |
| | | verify(connection, times(1)).bindAsync(any(BindRequest.class), any(IntermediateResponseHandler.class)); |
| | | |
| | | // Verify no heartbeat is sent because there is a bind in progress. |
| | | when(hbcf.timeService.now()).thenReturn(11001L); |
| | | scheduler.runAllTasks(); // Invokes HBCF.ConnectionImpl.sendHeartBeat() |
| | | verify(connection, times(1)).searchAsync(same(HEARTBEAT), any(SearchResultHandler.class)); |
| | | |
| | | // Send fake bind response, releasing the heartbeat. |
| | | when(hbcf.timeService.now()).thenReturn(11099L); |
| | | ((PromiseImpl) promise.getWrappedPromise()).handleResult(newResult(SUCCESS)); |
| | | |
| | | // Check that bind response acts as heartbeat. |
| | | assertThat(hbc.isValid()).isTrue(); |
| | | when(hbcf.timeService.now()).thenReturn(11100L); |
| | | scheduler.runAllTasks(); // Invokes HBCF.ConnectionImpl.checkForHeartBeat() |
| | | assertThat(hbc.isValid()).isTrue(); |
| | | } |
| | | |
| | | @Test(description = "OPENDJ-1348") |
| | | public void testBindTriggersHeartBeatTimeoutWhenTooSlow() throws Exception { |
| | | mockConnectionWithInitialHeartbeatResult(ResultCode.SUCCESS); |
| | | mockBindAsyncResponse(); |
| | | hbc = hbcf.getConnection(); |
| | | |
| | | // Send another bind request which will timeout. |
| | | when(hbcf.timeService.now()).thenReturn(20000L); |
| | | hbc.bindAsync(newSimpleBindRequest()); |
| | | verify(connection, times(1)).bindAsync(any(BindRequest.class), any(IntermediateResponseHandler.class)); |
| | | |
| | | // Verify no heartbeat is sent because there is a bind in progress. |
| | | when(hbcf.timeService.now()).thenReturn(20001L); |
| | | scheduler.runAllTasks(); // Invokes HBCF.ConnectionImpl.sendHeartBeat() |
| | | verify(connection, times(1)).searchAsync(same(HEARTBEAT), any(SearchResultHandler.class)); |
| | | |
| | | // Check that lack of bind response acts as heartbeat timeout. |
| | | assertThat(hbc.isValid()).isTrue(); |
| | | when(hbcf.timeService.now()).thenReturn(20100L); |
| | | scheduler.runAllTasks(); // Invokes HBCF.ConnectionImpl.checkForHeartBeat() |
| | | assertThat(hbc.isValid()).isFalse(); |
| | | } |
| | | |
| | | @SuppressWarnings({ "unchecked", "rawtypes" }) |
| | | @Test |
| | | public void testHeartBeatWhileBindInProgress() throws Exception { |
| | | mockConnectionWithInitialHeartbeatResult(ResultCode.SUCCESS); |
| | | //Trapping the callback of mockedConnection.bindAsync |
| | | BindResultLdapPromiseImpl promise = mockBindAsyncResponse(); |
| | | hbc = hbcf.getConnection(); |
| | | hbc.bindAsync(newSimpleBindRequest()); |
| | | |
| | | verify(connection, times(1)).bindAsync(any(BindRequest.class), any(IntermediateResponseHandler.class)); |
| | | |
| | | /* |
| | | * Now attempt the heartbeat which should not happen because there is a |
| | | * bind in progress. |
| | | */ |
| | | when(hbcf.timeService.now()).thenReturn(11000L); |
| | | // Attempt to send the heartbeat. |
| | | scheduler.runAllTasks(); |
| | | verify(connection, times(1)).searchAsync(same(HEARTBEAT), any(SearchResultHandler.class)); |
| | | |
| | | // Send fake bind response, releasing the heartbeat. |
| | | ((PromiseImpl) promise.getWrappedPromise()).handleResult(newResult(SUCCESS)); |
| | | |
| | | // Attempt to send a heartbeat again. |
| | | when(hbcf.timeService.now()).thenReturn(16000L); |
| | | // Attempt to send the heartbeat. |
| | | scheduler.runAllTasks(); |
| | | verify(connection, times(2)).searchAsync(same(HEARTBEAT), any(SearchResultHandler.class)); |
| | | } |
| | | |
| | | @Test |
| | | public void testToString() { |
| | | mockConnectionWithInitialHeartbeatResult(ResultCode.SUCCESS); |
| | | assertThat(hbcf.toString()).isNotNull(); |
| | | } |
| | | |
| | | private void checkInitialHeartBeatFailure(final LdapException e) { |
| | | /* |
| | | * Initial heartbeat failure should trigger connection exception with |
| | | * heartbeat cause. |
| | | */ |
| | | assertThat(e).isInstanceOf(ConnectionException.class); |
| | | assertThat(e.getResult().getResultCode()).isEqualTo(ResultCode.CLIENT_SIDE_SERVER_DOWN); |
| | | assertThat(e.getCause()).isInstanceOf(LdapException.class); |
| | | assertThat(((LdapException) e.getCause()).getResult().getResultCode()).isEqualTo(ResultCode.BUSY); |
| | | } |
| | | |
| | | private void mockConnectionWithInitialHeartbeatResult(final ResultCode initialHeartBeatResult) { |
| | | listeners = new LinkedList<ConnectionEventListener>(); |
| | | connection = mockConnection(listeners); |
| | | when(connection.isValid()).thenReturn(true); |
| | | mockHeartBeatResponse(connection, listeners, initialHeartBeatResult); |
| | | |
| | | // Underlying connection factory. |
| | | factory = mockConnectionFactory(connection); |
| | | |
| | | // Create heart beat connection factory. |
| | | scheduler = new MockScheduler(); |
| | | hbcf = new HeartBeatConnectionFactory(factory, 10000, 100, TimeUnit.MILLISECONDS, HEARTBEAT, scheduler); |
| | | |
| | | // Set initial time stamp. |
| | | hbcf.timeService = mockTimeService(0); |
| | | } |
| | | |
| | | private BindResultLdapPromiseImpl mockBindAsyncResponse() { |
| | | final BindResultLdapPromiseImpl bindPromise = newBindLdapPromise(-1, null, null, null, connection); |
| | | doAnswer(new Answer<LdapPromise<BindResult>>() { |
| | | @Override |
| | | public LdapPromise<BindResult> answer(final InvocationOnMock invocation) throws Throwable { |
| | | return bindPromise; |
| | | } |
| | | }).when(connection).bindAsync(any(BindRequest.class), any(IntermediateResponseHandler.class)); |
| | | |
| | | return bindPromise; |
| | | } |
| | | |
| | | private Connection mockHeartBeatResponse(final Connection mockConnection, |
| | | final List<ConnectionEventListener> listeners, final ResultCode resultCode) { |
| | | Answer<LdapPromise<Result>> answer = new Answer<LdapPromise<Result>>() { |
| | | @Override |
| | | public LdapPromise<Result> answer(final InvocationOnMock invocation) throws Throwable { |
| | | if (resultCode == null) { |
| | | return newLdapPromiseImpl(); |
| | | } |
| | | |
| | | if (resultCode.isExceptional()) { |
| | | final LdapException error = newLdapException(resultCode); |
| | | if (error instanceof ConnectionException) { |
| | | for (final ConnectionEventListener listener : listeners) { |
| | | listener.handleConnectionError(false, error); |
| | | } |
| | | } |
| | | return newFailedLdapPromise(error); |
| | | } else { |
| | | return newSuccessfulLdapPromise(newResult(resultCode)); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | doAnswer(answer).when(mockConnection).searchAsync(any(SearchRequest.class), any(SearchResultHandler.class)); |
| | | doAnswer(answer).when(mockConnection).searchAsync(any(SearchRequest.class), |
| | | any(IntermediateResponseHandler.class), any(SearchResultHandler.class)); |
| | | |
| | | return mockConnection; |
| | | } |
| | | |
| | | private void verifyHeartBeatSent(final Connection connection, final int times) { |
| | | verify(connection, times(times)).searchAsync(same(HEARTBEAT), any(SearchResultHandler.class)); |
| | | } |
| | | } |
| | |
| | | import com.forgerock.opendj.ldap.controls.AccountUsabilityRequestControl; |
| | | import com.forgerock.opendj.ldap.controls.AccountUsabilityResponseControl; |
| | | |
| | | import static org.forgerock.opendj.ldap.Connections.*; |
| | | import static org.forgerock.opendj.ldap.LdapException.*; |
| | | import static org.forgerock.opendj.ldap.TestCaseUtils.*; |
| | | |
| | |
| | | return; |
| | | } |
| | | sslContext = new SSLContextBuilder().getSSLContext(); |
| | | listener = newLDAPListener(findFreeSocketAddress(), getInstance(), new LDAPListenerOptions().setBacklog(4096)); |
| | | listener = |
| | | new LDAPListener(findFreeSocketAddress(), getInstance(), |
| | | new LDAPListenerOptions().setBacklog(4096)); |
| | | isRunning = true; |
| | | } |
| | | |
| | |
| | | * A mock scheduled executor which allows unit tests to directly invoke |
| | | * scheduled tasks without needing to wait. |
| | | */ |
| | | public final class MockScheduler implements ScheduledExecutorService { |
| | | final class MockScheduler implements ScheduledExecutorService { |
| | | |
| | | private final class ScheduledCallableFuture<T> implements ScheduledFuture<T>, Callable<T> { |
| | | private final Callable<T> callable; |
| | |
| | | /** Saved scheduled tasks. */ |
| | | private final List<Callable<?>> tasks = new CopyOnWriteArrayList<Callable<?>>(); |
| | | |
| | | public MockScheduler() { |
| | | MockScheduler() { |
| | | // Nothing to do. |
| | | } |
| | | |
| | |
| | | return tasks.get(0); |
| | | } |
| | | |
| | | public boolean isScheduled() { |
| | | boolean isScheduled() { |
| | | return !tasks.isEmpty(); |
| | | } |
| | | |
| | | public void runAllTasks() { |
| | | void runAllTasks() { |
| | | for (final Callable<?> task : tasks) { |
| | | runTask0(task); |
| | | } |
| | |
| | | * CDDL HEADER END |
| | | * |
| | | * |
| | | * Copyright 2014 ForgeRock AS |
| | | * Copyright 2010 Sun Microsystems, Inc. |
| | | * Portions Copyright 2011-2014 ForgeRock AS |
| | | */ |
| | | |
| | | package org.forgerock.opendj.grizzly; |
| | | |
| | | import java.io.IOException; |
| | | import java.security.GeneralSecurityException; |
| | | import java.util.List; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | import java.util.concurrent.CopyOnWriteArrayList; |
| | | import java.util.concurrent.TimeUnit; |
| | | import java.util.concurrent.atomic.AtomicBoolean; |
| | | import java.util.concurrent.atomic.AtomicInteger; |
| | | |
| | | import javax.net.ssl.SSLContext; |
| | | import javax.net.ssl.SSLEngine; |
| | | |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | | import org.forgerock.i18n.slf4j.LocalizedLogger; |
| | | import org.forgerock.opendj.io.LDAPWriter; |
| | | import org.forgerock.opendj.ldap.AbstractAsynchronousConnection; |
| | | import org.forgerock.opendj.ldap.ConnectionEventListener; |
| | | import org.forgerock.opendj.ldap.Connections; |
| | | import org.forgerock.opendj.ldap.IntermediateResponseHandler; |
| | | import org.forgerock.opendj.ldap.LDAPOptions; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.LdapPromise; |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.SSLContextBuilder; |
| | | import org.forgerock.opendj.ldap.SearchResultHandler; |
| | | import org.forgerock.opendj.ldap.TimeoutEventListener; |
| | | import org.forgerock.opendj.ldap.TrustManagers; |
| | | import org.forgerock.opendj.ldap.requests.AbandonRequest; |
| | | import org.forgerock.opendj.ldap.requests.AddRequest; |
| | | import org.forgerock.opendj.ldap.requests.BindClient; |
| | | import org.forgerock.opendj.ldap.requests.BindRequest; |
| | | import org.forgerock.opendj.ldap.requests.CompareRequest; |
| | | import org.forgerock.opendj.ldap.requests.DeleteRequest; |
| | | import org.forgerock.opendj.ldap.requests.ExtendedRequest; |
| | | import org.forgerock.opendj.ldap.requests.GenericBindRequest; |
| | | import org.forgerock.opendj.ldap.requests.Request; |
| | | import org.forgerock.opendj.ldap.requests.ModifyDNRequest; |
| | | import org.forgerock.opendj.ldap.requests.ModifyRequest; |
| | | import org.forgerock.opendj.ldap.requests.SearchRequest; |
| | | import org.forgerock.opendj.ldap.requests.StartTLSExtendedRequest; |
| | | import org.forgerock.opendj.ldap.requests.UnbindRequest; |
| | | import org.forgerock.opendj.ldap.responses.BindResult; |
| | | import org.forgerock.opendj.ldap.responses.CompareResult; |
| | | import org.forgerock.opendj.ldap.responses.ExtendedResult; |
| | | import org.forgerock.opendj.ldap.responses.Responses; |
| | | import org.forgerock.opendj.ldap.responses.Result; |
| | | import org.forgerock.opendj.ldap.spi.AbstractLdapConnectionImpl; |
| | | import org.forgerock.opendj.ldap.spi.BindResultLdapPromiseImpl; |
| | | import org.forgerock.opendj.ldap.spi.ExtendedResultLdapPromiseImpl; |
| | | import org.forgerock.opendj.ldap.spi.ResultLdapPromiseImpl; |
| | | import org.forgerock.opendj.ldap.spi.SearchResultLdapPromiseImpl; |
| | | import org.forgerock.util.Reject; |
| | | import org.glassfish.grizzly.CompletionHandler; |
| | | import org.glassfish.grizzly.filterchain.Filter; |
| | |
| | | import static org.forgerock.opendj.ldap.LdapException.*; |
| | | import static org.forgerock.opendj.ldap.spi.LdapPromises.*; |
| | | |
| | | final class GrizzlyLDAPConnection extends AbstractLdapConnectionImpl<GrizzlyLDAPConnectionFactory> { |
| | | import static com.forgerock.opendj.grizzly.GrizzlyMessages.*; |
| | | |
| | | /** |
| | | * LDAP connection implementation. |
| | | */ |
| | | final class GrizzlyLDAPConnection extends AbstractAsynchronousConnection implements TimeoutEventListener { |
| | | /** |
| | | * A dummy SSL client engine configurator as SSLFilter only needs client |
| | | * config. This prevents Grizzly from needlessly using JVM defaults which |
| | |
| | | private static final SSLEngineConfigurator DUMMY_SSL_ENGINE_CONFIGURATOR; |
| | | static { |
| | | try { |
| | | DUMMY_SSL_ENGINE_CONFIGURATOR = new SSLEngineConfigurator(new SSLContextBuilder().setTrustManager( |
| | | TrustManagers.distrustAll()).getSSLContext()); |
| | | DUMMY_SSL_ENGINE_CONFIGURATOR = |
| | | new SSLEngineConfigurator(new SSLContextBuilder().setTrustManager( |
| | | TrustManagers.distrustAll()).getSSLContext()); |
| | | } catch (GeneralSecurityException e) { |
| | | // This should never happen. |
| | | throw new IllegalStateException("Unable to create Dummy SSL Engine Configurator", e); |
| | | } |
| | | } |
| | | |
| | | private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); |
| | | private final AtomicBoolean bindOrStartTLSInProgress = new AtomicBoolean(false); |
| | | private final org.glassfish.grizzly.Connection<?> connection; |
| | | private final AtomicInteger nextMsgID = new AtomicInteger(1); |
| | | private final GrizzlyLDAPConnectionFactory factory; |
| | | private final ConcurrentHashMap<Integer, ResultLdapPromiseImpl<?, ?>> pendingRequests = |
| | | new ConcurrentHashMap<Integer, ResultLdapPromiseImpl<?, ?>>(); |
| | | private final Object stateLock = new Object(); |
| | | /** Guarded by stateLock. */ |
| | | private Result connectionInvalidReason; |
| | | private boolean failedDueToDisconnect; |
| | | private boolean isClosed; |
| | | private boolean isFailed; |
| | | private List<ConnectionEventListener> listeners; |
| | | |
| | | @SuppressWarnings("rawtypes") |
| | | public GrizzlyLDAPConnection(final org.glassfish.grizzly.Connection connection, |
| | | final GrizzlyLDAPConnectionFactory attachedFactory) { |
| | | super(attachedFactory); |
| | | /** |
| | | * Create a LDAP Connection with provided Grizzly connection and LDAP |
| | | * connection factory. |
| | | * |
| | | * @param connection |
| | | * actual connection |
| | | * @param factory |
| | | * factory that provides LDAP connections |
| | | */ |
| | | GrizzlyLDAPConnection(final org.glassfish.grizzly.Connection<?> connection, |
| | | final GrizzlyLDAPConnectionFactory factory) { |
| | | this.connection = connection; |
| | | this.factory = factory; |
| | | } |
| | | |
| | | @Override |
| | | protected LdapPromise<Void> abandonAsync0(final int messageID, final AbandonRequest request) { |
| | | public LdapPromise<Void> abandonAsync(final AbandonRequest request) { |
| | | /* |
| | | * Need to be careful here since both abandonAsync and Promise.cancel can |
| | | * be called separately by the client application. Therefore |
| | | * promise.cancel() should abandon the request, and abandonAsync should |
| | | * cancel the promise. In addition, bind or StartTLS requests cannot be |
| | | * abandoned. |
| | | */ |
| | | try { |
| | | synchronized (stateLock) { |
| | | checkConnectionIsValid(); |
| | | /* |
| | | * If there is a bind or startTLS in progress then it must be |
| | | * this request which is being abandoned. The following check |
| | | * will prevent it from happening. |
| | | */ |
| | | checkBindOrStartTLSInProgress(); |
| | | } |
| | | } catch (final LdapException e) { |
| | | return newFailedLdapPromise(e); |
| | | } |
| | | |
| | | // Remove the promise associated with the request to be abandoned. |
| | | final ResultLdapPromiseImpl<?, ?> pendingRequest = pendingRequests.remove(request.getRequestID()); |
| | | if (pendingRequest == null) { |
| | | /* |
| | | * There has never been a request with the specified message ID or |
| | | * the response has already been received and handled. We can ignore |
| | | * this abandon request. |
| | | */ |
| | | return newSuccessfulLdapPromise((Void) null); |
| | | } |
| | | |
| | | /* |
| | | * This will cancel the promise, but will also recursively invoke this |
| | | * method. Since the pending request has been removed, there is no risk |
| | | * of an infinite loop. |
| | | */ |
| | | pendingRequest.cancel(false); |
| | | |
| | | /* |
| | | * FIXME: there's a potential race condition here if a bind or startTLS |
| | | * is initiated just after we removed the pending request. |
| | | */ |
| | | return sendAbandonRequest(request); |
| | | } |
| | | |
| | | private LdapPromise<Void> sendAbandonRequest(final AbandonRequest request) { |
| | | final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter(); |
| | | try { |
| | | final int messageID = nextMsgID.getAndIncrement(); |
| | | writer.writeAbandonRequest(messageID, request); |
| | | connection.write(writer.getASN1Writer().getBuffer(), null); |
| | | return newSuccessfulLdapPromise((Void) null, messageID); |
| | | } catch (final IOException e) { |
| | | return newFailedLdapPromise(newLdapException(adaptRequestIOException(e))); |
| | | return newFailedLdapPromise(adaptRequestIOException(e)); |
| | | } finally { |
| | | GrizzlyUtils.recycleWriter(writer); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | protected void bindAsync0(final int messageID, final BindRequest request, final BindClient bindClient, |
| | | final IntermediateResponseHandler intermediateResponseHandler) throws LdapException { |
| | | checkConnectionIsValid(); |
| | | final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter(); |
| | | public LdapPromise<Result> addAsync(final AddRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | final int messageID = nextMsgID.getAndIncrement(); |
| | | final ResultLdapPromiseImpl<AddRequest, Result> promise = |
| | | newResultLdapPromise(messageID, request, intermediateResponseHandler, this); |
| | | try { |
| | | // Use the bind client to get the initial request instead of |
| | | // using the bind request passed to this method. |
| | | final GenericBindRequest initialRequest = bindClient.nextBindRequest(); |
| | | writer.writeBindRequest(messageID, 3, initialRequest); |
| | | connection.write(writer.getASN1Writer().getBuffer(), null); |
| | | } catch (final IOException e) { |
| | | throw newLdapException(adaptRequestIOException(e)); |
| | | } finally { |
| | | GrizzlyUtils.recycleWriter(writer); |
| | | synchronized (stateLock) { |
| | | checkConnectionIsValid(); |
| | | checkBindOrStartTLSInProgress(); |
| | | pendingRequests.put(messageID, promise); |
| | | } |
| | | try { |
| | | final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter(); |
| | | try { |
| | | writer.writeAddRequest(messageID, request); |
| | | connection.write(writer.getASN1Writer().getBuffer(), null); |
| | | } finally { |
| | | GrizzlyUtils.recycleWriter(writer); |
| | | } |
| | | } catch (final IOException e) { |
| | | pendingRequests.remove(messageID); |
| | | throw adaptRequestIOException(e); |
| | | } |
| | | } catch (final LdapException e) { |
| | | promise.adaptErrorResult(e.getResult()); |
| | | } |
| | | return promise; |
| | | } |
| | | |
| | | @Override |
| | | public void addConnectionEventListener(final ConnectionEventListener listener) { |
| | | Reject.ifNull(listener); |
| | | final boolean notifyClose; |
| | | final boolean notifyErrorOccurred; |
| | | synchronized (stateLock) { |
| | | notifyClose = isClosed; |
| | | notifyErrorOccurred = isFailed; |
| | | if (!isClosed) { |
| | | if (listeners == null) { |
| | | listeners = new CopyOnWriteArrayList<ConnectionEventListener>(); |
| | | } |
| | | listeners.add(listener); |
| | | } |
| | | } |
| | | if (notifyErrorOccurred) { |
| | | // Use the reason provided in the disconnect notification. |
| | | listener.handleConnectionError(failedDueToDisconnect, |
| | | newLdapException(connectionInvalidReason)); |
| | | } |
| | | if (notifyClose) { |
| | | listener.handleConnectionClosed(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | protected void close0(final int messageID, UnbindRequest unbindRequest, String reason) { |
| | | Reject.ifNull(unbindRequest); |
| | | // This is the final client initiated close then release the connection |
| | | // and release resources. |
| | | final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter(); |
| | | public LdapPromise<BindResult> bindAsync(final BindRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | final int messageID = nextMsgID.getAndIncrement(); |
| | | final BindClient context; |
| | | try { |
| | | writer.writeUnbindRequest(messageID, unbindRequest); |
| | | connection.write(writer.getASN1Writer().getBuffer(), null); |
| | | } catch (final Exception ignore) { |
| | | /* |
| | | * Underlying channel probably blown up. Ignore all errors, |
| | | * including possibly runtime exceptions (see OPENDJ-672). |
| | | */ |
| | | } finally { |
| | | GrizzlyUtils.recycleWriter(writer); |
| | | context = request.createBindClient(Connections.getHostString(factory.getSocketAddress())); |
| | | } catch (final LdapException e) { |
| | | return newFailedLdapPromise(e, messageID); |
| | | } |
| | | getFactory().getTimeoutChecker().removeListener(this); |
| | | connection.closeSilently(); |
| | | |
| | | final BindResultLdapPromiseImpl promise = |
| | | newBindLdapPromise(messageID, request, context, intermediateResponseHandler, this); |
| | | |
| | | try { |
| | | synchronized (stateLock) { |
| | | checkConnectionIsValid(); |
| | | if (!pendingRequests.isEmpty()) { |
| | | promise.setResultOrError(Responses.newBindResult(ResultCode.OPERATIONS_ERROR).setDiagnosticMessage( |
| | | "There are other operations pending on this connection")); |
| | | return promise; |
| | | } |
| | | if (!bindOrStartTLSInProgress.compareAndSet(false, true)) { |
| | | promise.setResultOrError(Responses.newBindResult(ResultCode.OPERATIONS_ERROR).setDiagnosticMessage( |
| | | "Bind or Start TLS operation in progress")); |
| | | return promise; |
| | | } |
| | | pendingRequests.put(messageID, promise); |
| | | } |
| | | |
| | | try { |
| | | final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter(); |
| | | try { |
| | | // Use the bind client to get the initial request instead of |
| | | // using the bind request passed to this method. |
| | | final GenericBindRequest initialRequest = context.nextBindRequest(); |
| | | writer.writeBindRequest(messageID, 3, initialRequest); |
| | | connection.write(writer.getASN1Writer().getBuffer(), null); |
| | | } finally { |
| | | GrizzlyUtils.recycleWriter(writer); |
| | | } |
| | | } catch (final IOException e) { |
| | | pendingRequests.remove(messageID); |
| | | bindOrStartTLSInProgress.set(false); |
| | | throw adaptRequestIOException(e); |
| | | } |
| | | } catch (final LdapException e) { |
| | | promise.adaptErrorResult(e.getResult()); |
| | | } |
| | | |
| | | return promise; |
| | | } |
| | | |
| | | @Override |
| | | protected <R extends Request> void writeRequest(final int messageID, final R request, |
| | | final IntermediateResponseHandler intermediateResponseHandler, final RequestWriter<R> requestWriter) { |
| | | final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter(); |
| | | public void close(final UnbindRequest request, final String reason) { |
| | | // FIXME: I18N need to internationalize this message. |
| | | Reject.ifNull(request); |
| | | close(request, false, Responses.newResult(ResultCode.CLIENT_SIDE_USER_CANCELLED) |
| | | .setDiagnosticMessage(reason != null ? reason : "Connection closed by client")); |
| | | } |
| | | |
| | | @Override |
| | | public LdapPromise<CompareResult> compareAsync(final CompareRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | final int messageID = nextMsgID.getAndIncrement(); |
| | | final ResultLdapPromiseImpl<CompareRequest, CompareResult> promise = |
| | | newCompareLdapPromise(messageID, request, intermediateResponseHandler, this); |
| | | try { |
| | | requestWriter.writeRequest(messageID, writer, request); |
| | | connection.write(writer.getASN1Writer().getBuffer(), null); |
| | | } catch (final IOException e) { |
| | | removePendingResult(messageID).setResultOrError(adaptRequestIOException(e)); |
| | | } finally { |
| | | GrizzlyUtils.recycleWriter(writer); |
| | | synchronized (stateLock) { |
| | | checkConnectionIsValid(); |
| | | checkBindOrStartTLSInProgress(); |
| | | pendingRequests.put(messageID, promise); |
| | | } |
| | | try { |
| | | final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter(); |
| | | try { |
| | | writer.writeCompareRequest(messageID, request); |
| | | connection.write(writer.getASN1Writer().getBuffer(), null); |
| | | } finally { |
| | | GrizzlyUtils.recycleWriter(writer); |
| | | } |
| | | } catch (final IOException e) { |
| | | pendingRequests.remove(messageID); |
| | | throw adaptRequestIOException(e); |
| | | } |
| | | } catch (final LdapException e) { |
| | | promise.adaptErrorResult(e.getResult()); |
| | | } |
| | | return promise; |
| | | } |
| | | |
| | | @Override |
| | | public LdapPromise<Result> deleteAsync(final DeleteRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | final int messageID = nextMsgID.getAndIncrement(); |
| | | final ResultLdapPromiseImpl<DeleteRequest, Result> promise = |
| | | newResultLdapPromise(messageID, request, intermediateResponseHandler, this); |
| | | try { |
| | | synchronized (stateLock) { |
| | | checkConnectionIsValid(); |
| | | checkBindOrStartTLSInProgress(); |
| | | pendingRequests.put(messageID, promise); |
| | | } |
| | | try { |
| | | final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter(); |
| | | try { |
| | | writer.writeDeleteRequest(messageID, request); |
| | | connection.write(writer.getASN1Writer().getBuffer(), null); |
| | | } finally { |
| | | GrizzlyUtils.recycleWriter(writer); |
| | | } |
| | | } catch (final IOException e) { |
| | | pendingRequests.remove(messageID); |
| | | throw adaptRequestIOException(e); |
| | | } |
| | | } catch (final LdapException e) { |
| | | promise.adaptErrorResult(e.getResult()); |
| | | } |
| | | return promise; |
| | | } |
| | | |
| | | @Override |
| | | public <R extends ExtendedResult> LdapPromise<R> extendedRequestAsync(final ExtendedRequest<R> request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | final int messageID = nextMsgID.getAndIncrement(); |
| | | final ExtendedResultLdapPromiseImpl<R> promise = |
| | | newExtendedLdapPromise(messageID, request, intermediateResponseHandler, this); |
| | | try { |
| | | synchronized (stateLock) { |
| | | checkConnectionIsValid(); |
| | | if (StartTLSExtendedRequest.OID.equals(request.getOID())) { |
| | | if (!pendingRequests.isEmpty()) { |
| | | promise.setResultOrError(request.getResultDecoder().newExtendedErrorResult( |
| | | ResultCode.OPERATIONS_ERROR, "", "There are pending operations on this connection")); |
| | | return promise; |
| | | } else if (isTLSEnabled()) { |
| | | promise.setResultOrError(request.getResultDecoder().newExtendedErrorResult( |
| | | ResultCode.OPERATIONS_ERROR, "", "This connection is already TLS enabled")); |
| | | return promise; |
| | | } else if (!bindOrStartTLSInProgress.compareAndSet(false, true)) { |
| | | promise.setResultOrError(request.getResultDecoder().newExtendedErrorResult( |
| | | ResultCode.OPERATIONS_ERROR, "", "Bind or Start TLS operation in progress")); |
| | | return promise; |
| | | } |
| | | } else { |
| | | checkBindOrStartTLSInProgress(); |
| | | } |
| | | pendingRequests.put(messageID, promise); |
| | | } |
| | | try { |
| | | final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter(); |
| | | try { |
| | | writer.writeExtendedRequest(messageID, request); |
| | | connection.write(writer.getASN1Writer().getBuffer(), null); |
| | | } finally { |
| | | GrizzlyUtils.recycleWriter(writer); |
| | | } |
| | | } catch (final IOException e) { |
| | | pendingRequests.remove(messageID); |
| | | bindOrStartTLSInProgress.set(false); |
| | | throw adaptRequestIOException(e); |
| | | } |
| | | } catch (final LdapException e) { |
| | | promise.adaptErrorResult(e.getResult()); |
| | | } |
| | | return promise; |
| | | } |
| | | |
| | | @Override |
| | | public boolean isClosed() { |
| | | synchronized (stateLock) { |
| | | return isClosed; |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public boolean isValid() { |
| | | synchronized (stateLock) { |
| | | return isValid0(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public LdapPromise<Result> modifyAsync(final ModifyRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | final int messageID = nextMsgID.getAndIncrement(); |
| | | final ResultLdapPromiseImpl<ModifyRequest, Result> promise = |
| | | newResultLdapPromise(messageID, request, intermediateResponseHandler, this); |
| | | try { |
| | | synchronized (stateLock) { |
| | | checkConnectionIsValid(); |
| | | checkBindOrStartTLSInProgress(); |
| | | pendingRequests.put(messageID, promise); |
| | | } |
| | | try { |
| | | final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter(); |
| | | try { |
| | | writer.writeModifyRequest(messageID, request); |
| | | connection.write(writer.getASN1Writer().getBuffer(), null); |
| | | } finally { |
| | | GrizzlyUtils.recycleWriter(writer); |
| | | } |
| | | } catch (final IOException e) { |
| | | pendingRequests.remove(messageID); |
| | | throw adaptRequestIOException(e); |
| | | } |
| | | } catch (final LdapException e) { |
| | | promise.adaptErrorResult(e.getResult()); |
| | | } |
| | | return promise; |
| | | } |
| | | |
| | | @Override |
| | | public LdapPromise<Result> modifyDNAsync(final ModifyDNRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler) { |
| | | final int messageID = nextMsgID.getAndIncrement(); |
| | | final ResultLdapPromiseImpl<ModifyDNRequest, Result> promise = |
| | | newResultLdapPromise(messageID, request, intermediateResponseHandler, this); |
| | | try { |
| | | synchronized (stateLock) { |
| | | checkConnectionIsValid(); |
| | | checkBindOrStartTLSInProgress(); |
| | | pendingRequests.put(messageID, promise); |
| | | } |
| | | try { |
| | | final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter(); |
| | | try { |
| | | writer.writeModifyDNRequest(messageID, request); |
| | | connection.write(writer.getASN1Writer().getBuffer(), null); |
| | | } finally { |
| | | GrizzlyUtils.recycleWriter(writer); |
| | | } |
| | | } catch (final IOException e) { |
| | | pendingRequests.remove(messageID); |
| | | throw adaptRequestIOException(e); |
| | | } |
| | | } catch (final LdapException e) { |
| | | promise.adaptErrorResult(e.getResult()); |
| | | } |
| | | return promise; |
| | | } |
| | | |
| | | @Override |
| | | public void removeConnectionEventListener(final ConnectionEventListener listener) { |
| | | Reject.ifNull(listener); |
| | | synchronized (stateLock) { |
| | | if (listeners != null) { |
| | | listeners.remove(listener); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public LdapPromise<Result> searchAsync(final SearchRequest request, |
| | | final IntermediateResponseHandler intermediateResponseHandler, final SearchResultHandler entryHandler) { |
| | | final int messageID = nextMsgID.getAndIncrement(); |
| | | final SearchResultLdapPromiseImpl promise = |
| | | newSearchLdapPromise(messageID, request, entryHandler, intermediateResponseHandler, this); |
| | | try { |
| | | synchronized (stateLock) { |
| | | checkConnectionIsValid(); |
| | | checkBindOrStartTLSInProgress(); |
| | | pendingRequests.put(messageID, promise); |
| | | } |
| | | try { |
| | | final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter(); |
| | | try { |
| | | writer.writeSearchRequest(messageID, request); |
| | | connection.write(writer.getASN1Writer().getBuffer(), null); |
| | | } finally { |
| | | GrizzlyUtils.recycleWriter(writer); |
| | | } |
| | | } catch (final IOException e) { |
| | | pendingRequests.remove(messageID); |
| | | throw adaptRequestIOException(e); |
| | | } |
| | | } catch (final LdapException e) { |
| | | promise.adaptErrorResult(e.getResult()); |
| | | } |
| | | return promise; |
| | | } |
| | | |
| | | @Override |
| | | public String toString() { |
| | | final StringBuilder builder = new StringBuilder(); |
| | | builder.append("LDAPConnection("); |
| | | builder.append(connection.getLocalAddress()); |
| | | builder.append(','); |
| | | builder.append(connection.getPeerAddress()); |
| | | builder.append(')'); |
| | | return builder.toString(); |
| | | } |
| | | |
| | | @Override |
| | | public long handleTimeout(final long currentTime) { |
| | | final long timeout = factory.getLDAPOptions().getTimeout(TimeUnit.MILLISECONDS); |
| | | if (timeout <= 0) { |
| | | return 0; |
| | | } |
| | | |
| | | long delay = timeout; |
| | | for (final ResultLdapPromiseImpl<?, ?> promise : pendingRequests.values()) { |
| | | if (promise == null || !promise.checkForTimeout()) { |
| | | continue; |
| | | } |
| | | final long diff = (promise.getTimestamp() + timeout) - currentTime; |
| | | if (diff > 0) { |
| | | // Will expire in diff milliseconds. |
| | | delay = Math.min(delay, diff); |
| | | } else if (pendingRequests.remove(promise.getRequestID()) == null) { |
| | | // Result arrived at the same time. |
| | | continue; |
| | | } else if (promise.isBindOrStartTLS()) { |
| | | /* |
| | | * No other operations can be performed while a bind or StartTLS |
| | | * request is active, so we cannot time out the request. We |
| | | * therefore have a choice: either ignore timeouts for these |
| | | * operations, or enforce them but doing so requires |
| | | * invalidating the connection. We'll do the latter, since |
| | | * ignoring timeouts could cause the application to hang. |
| | | */ |
| | | logger.debug(LocalizableMessage.raw("Failing bind or StartTLS request due to timeout %s" |
| | | + "(connection will be invalidated): ", promise)); |
| | | final Result result = Responses.newResult(ResultCode.CLIENT_SIDE_TIMEOUT).setDiagnosticMessage( |
| | | LDAP_CONNECTION_BIND_OR_START_TLS_REQUEST_TIMEOUT.get(timeout).toString()); |
| | | promise.adaptErrorResult(result); |
| | | |
| | | // Fail the connection. |
| | | final Result errorResult = Responses.newResult(ResultCode.CLIENT_SIDE_TIMEOUT).setDiagnosticMessage( |
| | | LDAP_CONNECTION_BIND_OR_START_TLS_CONNECTION_TIMEOUT.get(timeout).toString()); |
| | | connectionErrorOccurred(errorResult); |
| | | } else { |
| | | logger.debug(LocalizableMessage.raw("Failing request due to timeout: %s", promise)); |
| | | final Result result = Responses.newResult(ResultCode.CLIENT_SIDE_TIMEOUT).setDiagnosticMessage( |
| | | LDAP_CONNECTION_REQUEST_TIMEOUT.get(timeout).toString()); |
| | | promise.adaptErrorResult(result); |
| | | |
| | | /* |
| | | * FIXME: there's a potential race condition here if a bind or |
| | | * startTLS is initiated just after we check the boolean. It |
| | | * seems potentially even more dangerous to send the abandon |
| | | * request while holding the state lock, since a blocking write |
| | | * could hang the application. |
| | | */ |
| | | // if (!bindOrStartTLSInProgress.get()) { |
| | | // sendAbandonRequest(newAbandonRequest(promise.getRequestID())); |
| | | // } |
| | | } |
| | | } |
| | | return delay; |
| | | } |
| | | |
| | | @Override |
| | | public long getTimeout() { |
| | | return factory.getLDAPOptions().getTimeout(TimeUnit.MILLISECONDS); |
| | | } |
| | | |
| | | /** |
| | | * Closes this connection, invoking event listeners as needed. |
| | | * |
| | | * @param unbindRequest |
| | | * The client provided unbind request if this is a client |
| | | * initiated close, or {@code null} if the connection has failed. |
| | | * @param isDisconnectNotification |
| | | * {@code true} if this is a connection failure signalled by a |
| | | * server disconnect notification. |
| | | * @param reason |
| | | * The result indicating why the connection was closed. |
| | | */ |
| | | void close(final UnbindRequest unbindRequest, final boolean isDisconnectNotification, |
| | | final Result reason) { |
| | | final boolean notifyClose; |
| | | final boolean notifyErrorOccurred; |
| | | final List<ConnectionEventListener> tmpListeners; |
| | | synchronized (stateLock) { |
| | | if (isClosed) { |
| | | // Already closed locally. |
| | | return; |
| | | } else if (unbindRequest != null) { |
| | | // Local close. |
| | | notifyClose = true; |
| | | notifyErrorOccurred = false; |
| | | isClosed = true; |
| | | tmpListeners = listeners; |
| | | listeners = null; // Prevent future invocations. |
| | | if (connectionInvalidReason == null) { |
| | | connectionInvalidReason = reason; |
| | | } |
| | | } else if (isFailed) { |
| | | // Already failed. |
| | | return; |
| | | } else { |
| | | // Connection has failed and this is the first indication. |
| | | notifyClose = false; |
| | | notifyErrorOccurred = true; |
| | | isFailed = true; |
| | | failedDueToDisconnect = isDisconnectNotification; |
| | | connectionInvalidReason = reason; |
| | | tmpListeners = listeners; // Keep list for client close. |
| | | } |
| | | } |
| | | |
| | | // First abort all outstanding requests. |
| | | for (final int requestID : pendingRequests.keySet()) { |
| | | final ResultLdapPromiseImpl<?, ?> promise = pendingRequests.remove(requestID); |
| | | if (promise != null) { |
| | | promise.adaptErrorResult(connectionInvalidReason); |
| | | } |
| | | } |
| | | |
| | | /* |
| | | * If this is the final client initiated close then release close the |
| | | * connection and release resources. |
| | | */ |
| | | if (notifyClose) { |
| | | final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter(); |
| | | try { |
| | | writer.writeUnbindRequest(nextMsgID.getAndIncrement(), unbindRequest); |
| | | connection.write(writer.getASN1Writer().getBuffer(), null); |
| | | } catch (final Exception ignore) { |
| | | /* |
| | | * Underlying channel probably blown up. Ignore all errors, |
| | | * including possibly runtime exceptions (see OPENDJ-672). |
| | | */ |
| | | } finally { |
| | | GrizzlyUtils.recycleWriter(writer); |
| | | } |
| | | factory.getTimeoutChecker().removeListener(this); |
| | | connection.closeSilently(); |
| | | factory.releaseTransportAndTimeoutChecker(); |
| | | } |
| | | |
| | | // Notify listeners. |
| | | if (tmpListeners != null) { |
| | | if (notifyErrorOccurred) { |
| | | for (final ConnectionEventListener listener : tmpListeners) { |
| | | // Use the reason provided in the disconnect notification. |
| | | listener.handleConnectionError(isDisconnectNotification, newLdapException(reason)); |
| | | } |
| | | } |
| | | if (notifyClose) { |
| | | for (final ConnectionEventListener listener : tmpListeners) { |
| | | listener.handleConnectionClosed(); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | int continuePendingBindRequest(final BindResultLdapPromiseImpl promise) throws LdapException { |
| | | final int newMsgID = nextMsgID.getAndIncrement(); |
| | | synchronized (stateLock) { |
| | | checkConnectionIsValid(); |
| | | pendingRequests.put(newMsgID, promise); |
| | | } |
| | | return newMsgID; |
| | | } |
| | | |
| | | LDAPOptions getLDAPOptions() { |
| | | return factory.getLDAPOptions(); |
| | | } |
| | | |
| | | ResultLdapPromiseImpl<?, ?> getPendingRequest(final Integer messageID) { |
| | | return pendingRequests.get(messageID); |
| | | } |
| | | |
| | | void handleUnsolicitedNotification(final ExtendedResult result) { |
| | | final List<ConnectionEventListener> tmpListeners; |
| | | synchronized (stateLock) { |
| | | tmpListeners = listeners; |
| | | } |
| | | if (tmpListeners != null) { |
| | | for (final ConnectionEventListener listener : tmpListeners) { |
| | | listener.handleUnsolicitedNotification(result); |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | * The filter to be installed. |
| | | */ |
| | | void installFilter(final Filter filter) { |
| | | synchronized (state) { |
| | | synchronized (stateLock) { |
| | | GrizzlyUtils.addFilterToConnection(filter, connection); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | protected boolean isTLSEnabled() { |
| | | synchronized (state) { |
| | | /** |
| | | * Indicates whether or not TLS is enabled on this connection. |
| | | * |
| | | * @return {@code true} if TLS is enabled on this connection, otherwise |
| | | * {@code false}. |
| | | */ |
| | | boolean isTLSEnabled() { |
| | | synchronized (stateLock) { |
| | | final FilterChain currentFilterChain = (FilterChain) connection.getProcessor(); |
| | | for (final Filter filter : currentFilterChain) { |
| | | if (filter instanceof SSLFilter) { |
| | |
| | | } |
| | | } |
| | | |
| | | ResultLdapPromiseImpl<?, ?> removePendingRequest(final Integer messageID) { |
| | | return pendingRequests.remove(messageID); |
| | | } |
| | | |
| | | void setBindOrStartTLSInProgress(final boolean state) { |
| | | bindOrStartTLSInProgress.set(state); |
| | | } |
| | | |
| | | void startTLS(final SSLContext sslContext, final List<String> protocols, final List<String> cipherSuites, |
| | | final CompletionHandler<SSLEngine> completionHandler) throws IOException { |
| | | synchronized (state) { |
| | | synchronized (stateLock) { |
| | | if (isTLSEnabled()) { |
| | | throw new IllegalStateException("TLS already enabled"); |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | private Result adaptRequestIOException(final IOException e) { |
| | | private LdapException adaptRequestIOException(final IOException e) { |
| | | // FIXME: what other sort of IOExceptions can be thrown? |
| | | // FIXME: Is this the best result code? |
| | | final Result errorResult = Responses.newResult(ResultCode.CLIENT_SIDE_ENCODING_ERROR).setCause(e); |
| | | connectionErrorOccurred(false, errorResult); |
| | | return errorResult; |
| | | connectionErrorOccurred(errorResult); |
| | | return newLdapException(errorResult); |
| | | } |
| | | |
| | | @Override |
| | | protected ResultLdapPromiseImpl<?, ?> getPendingResult(int messageID) { |
| | | return super.getPendingResult(messageID); |
| | | }; |
| | | |
| | | @Override |
| | | protected <R extends Request, S extends Result> ResultLdapPromiseImpl<R, S> removePendingResult( |
| | | final Integer messageID) { |
| | | return super.removePendingResult(messageID); |
| | | private void checkBindOrStartTLSInProgress() throws LdapException { |
| | | if (bindOrStartTLSInProgress.get()) { |
| | | throw newLdapException(ResultCode.OPERATIONS_ERROR, "Bind or Start TLS operation in progress"); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | protected void connectionErrorOccurred(boolean isDisconnectNotification, Result reason) { |
| | | super.connectionErrorOccurred(isDisconnectNotification, reason); |
| | | private void checkConnectionIsValid() throws LdapException { |
| | | if (!isValid0()) { |
| | | if (failedDueToDisconnect) { |
| | | /* |
| | | * Connection termination was triggered remotely. We don't want |
| | | * to blindly pass on the result code to requests since it could |
| | | * be confused for a genuine response. For example, if the |
| | | * disconnect contained the invalidCredentials result code then |
| | | * this could be misinterpreted as a genuine authentication |
| | | * failure for subsequent bind requests. |
| | | */ |
| | | throw newLdapException(ResultCode.CLIENT_SIDE_SERVER_DOWN, "Connection closed by server"); |
| | | } else { |
| | | throw newLdapException(connectionInvalidReason); |
| | | } |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | protected void handleUnsolicitedNotification(final ExtendedResult notification) { |
| | | super.handleUnsolicitedNotification(notification); |
| | | private void connectionErrorOccurred(final Result reason) { |
| | | close(null, false, reason); |
| | | } |
| | | |
| | | int continuePendingBindRequest(BindResultLdapPromiseImpl promise) |
| | | throws LdapException { |
| | | final int newMsgID = newMessageID(); |
| | | checkConnectionIsValid(); |
| | | addPendingResult(newMsgID, promise); |
| | | |
| | | return newMsgID; |
| | | private boolean isValid0() { |
| | | return !isFailed && !isClosed; |
| | | } |
| | | } |
| | |
| | | package org.forgerock.opendj.grizzly; |
| | | |
| | | import java.io.IOException; |
| | | import java.net.InetSocketAddress; |
| | | import java.util.concurrent.ExecutionException; |
| | | import java.util.concurrent.TimeUnit; |
| | | import java.util.concurrent.atomic.AtomicBoolean; |
| | | import java.util.concurrent.atomic.AtomicInteger; |
| | | |
| | | import javax.net.ssl.SSLEngine; |
| | | |
| | |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.TimeoutChecker; |
| | | import org.forgerock.opendj.ldap.TimeoutEventListener; |
| | | import org.forgerock.opendj.ldap.spi.AbstractLdapConnectionFactoryImpl; |
| | | import org.forgerock.opendj.ldap.spi.AbstractLdapConnectionImpl; |
| | | import org.forgerock.opendj.ldap.requests.Requests; |
| | | import org.forgerock.opendj.ldap.requests.StartTLSExtendedRequest; |
| | | import org.forgerock.opendj.ldap.responses.ExtendedResult; |
| | | import org.forgerock.opendj.ldap.spi.LDAPConnectionFactoryImpl; |
| | | import org.forgerock.util.promise.Function; |
| | | import org.forgerock.util.promise.FailureHandler; |
| | | import org.forgerock.util.promise.Promise; |
| | | import org.forgerock.util.promise.PromiseImpl; |
| | | import org.forgerock.util.promise.SuccessHandler; |
| | | import org.glassfish.grizzly.CompletionHandler; |
| | | import org.glassfish.grizzly.EmptyCompletionHandler; |
| | | import org.glassfish.grizzly.SocketConnectorHandler; |
| | | import org.glassfish.grizzly.filterchain.FilterChain; |
| | |
| | | /** |
| | | * LDAP connection factory implementation using Grizzly for transport. |
| | | */ |
| | | public final class GrizzlyLDAPConnectionFactory extends AbstractLdapConnectionFactoryImpl implements |
| | | LDAPConnectionFactoryImpl { |
| | | public final class GrizzlyLDAPConnectionFactory implements LDAPConnectionFactoryImpl { |
| | | private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); |
| | | |
| | | /** |
| | | * Adapts a Grizzly connection completion handler to an LDAP connection |
| | | * promise. |
| | | * Adapts a Grizzly connection completion handler to an LDAP connection promise. |
| | | */ |
| | | @SuppressWarnings("rawtypes") |
| | | private final class CompletionHandlerAdapter extends EmptyCompletionHandler<org.glassfish.grizzly.Connection> |
| | | implements TimeoutEventListener { |
| | | private final PromiseImpl<org.glassfish.grizzly.Connection, LdapException> promise; |
| | | private final class CompletionHandlerAdapter implements |
| | | CompletionHandler<org.glassfish.grizzly.Connection>, TimeoutEventListener { |
| | | private final PromiseImpl<Connection, LdapException> promise; |
| | | private final long timeoutEndTime; |
| | | |
| | | private CompletionHandlerAdapter(final PromiseImpl<org.glassfish.grizzly.Connection, LdapException> promise) { |
| | | private CompletionHandlerAdapter(final PromiseImpl<Connection, LdapException> promise) { |
| | | this.promise = promise; |
| | | final long timeoutMS = getTimeout(); |
| | | this.timeoutEndTime = timeoutMS > 0 ? System.currentTimeMillis() + timeoutMS : 0; |
| | |
| | | } |
| | | |
| | | @Override |
| | | public void completed(final org.glassfish.grizzly.Connection connection) { |
| | | timeoutChecker.get().removeListener(this); |
| | | if (!promise.tryHandleResult(connection)) { |
| | | // The connection has been either cancelled or it has timed out. |
| | | public void cancelled() { |
| | | // Ignore this. |
| | | } |
| | | |
| | | @Override |
| | | public void completed(final org.glassfish.grizzly.Connection result) { |
| | | // Adapt the connection. |
| | | final GrizzlyLDAPConnection connection = adaptConnection(result); |
| | | |
| | | // Plain connection. |
| | | if (options.getSSLContext() == null) { |
| | | onSuccess(connection); |
| | | return; |
| | | } |
| | | |
| | | // Start TLS or install SSL layer asynchronously. |
| | | |
| | | // Give up immediately if the promise has been cancelled or timed out. |
| | | if (promise.isDone()) { |
| | | timeoutChecker.get().removeListener(this); |
| | | connection.close(); |
| | | return; |
| | | } |
| | | |
| | | if (options.useStartTLS()) { |
| | | // Chain StartTLS extended request. |
| | | final StartTLSExtendedRequest startTLS = |
| | | Requests.newStartTLSExtendedRequest(options.getSSLContext()); |
| | | startTLS.addEnabledCipherSuite(options.getEnabledCipherSuites().toArray( |
| | | new String[options.getEnabledCipherSuites().size()])); |
| | | startTLS.addEnabledProtocol(options.getEnabledProtocols().toArray( |
| | | new String[options.getEnabledProtocols().size()])); |
| | | |
| | | connection.extendedRequestAsync(startTLS).onSuccess(new SuccessHandler<ExtendedResult>() { |
| | | @Override |
| | | public void handleResult(final ExtendedResult result) { |
| | | onSuccess(connection); |
| | | } |
| | | }).onFailure(new FailureHandler<LdapException>() { |
| | | @Override |
| | | public void handleError(final LdapException error) { |
| | | onFailure(connection, error); |
| | | } |
| | | }); |
| | | } else { |
| | | // Install SSL/TLS layer. |
| | | try { |
| | | connection.startTLS(options.getSSLContext(), options.getEnabledProtocols(), |
| | | options.getEnabledCipherSuites(), new EmptyCompletionHandler<SSLEngine>() { |
| | | @Override |
| | | public void completed(final SSLEngine result) { |
| | | onSuccess(connection); |
| | | } |
| | | |
| | | @Override |
| | | public void failed(final Throwable throwable) { |
| | | onFailure(connection, throwable); |
| | | } |
| | | }); |
| | | } catch (final IOException e) { |
| | | onFailure(connection, e); |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | // Adapt and forward. |
| | | timeoutChecker.get().removeListener(this); |
| | | promise.handleError(adaptConnectionException(throwable)); |
| | | releaseTransportAndTimeoutChecker(); |
| | | } |
| | | |
| | | @Override |
| | | public void updated(final org.glassfish.grizzly.Connection result) { |
| | | // Ignore this. |
| | | } |
| | | |
| | | 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); |
| | | timeoutChecker.get().addListener(ldapConnection); |
| | | clientFilter.registerConnection(connection, ldapConnection); |
| | | return ldapConnection; |
| | | } |
| | | |
| | | private LdapException adaptConnectionException(Throwable t) { |
| | | if (!(t instanceof LdapException) && t instanceof ExecutionException) { |
| | | t = t.getCause() != null ? t.getCause() : t; |
| | | } |
| | | |
| | | if (t instanceof LdapException) { |
| | | return (LdapException) t; |
| | | } else { |
| | | return newLdapException(ResultCode.CLIENT_SIDE_CONNECT_ERROR, t.getMessage(), t); |
| | | } |
| | | } |
| | | |
| | | private void onFailure(final GrizzlyLDAPConnection connection, final Throwable t) { |
| | | // Abort connection attempt due to error. |
| | | timeoutChecker.get().removeListener(this); |
| | | promise.handleError(adaptConnectionException(t)); |
| | | connection.close(); |
| | | } |
| | | |
| | | private void onSuccess(final GrizzlyLDAPConnection connection) { |
| | | timeoutChecker.get().removeListener(this); |
| | | if (!promise.tryHandleResult(connection)) { |
| | | // The connection has been either cancelled or it has timed out. |
| | | connection.close(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | |
| | | |
| | | private final LDAPClientFilter clientFilter; |
| | | private final FilterChain defaultFilterChain; |
| | | private final ReferenceCountedObject<TCPNIOTransport>.Reference transport; |
| | | private final ReferenceCountedObject<TimeoutChecker>.Reference timeoutChecker = TIMEOUT_CHECKER.acquire(); |
| | | private final LDAPOptions options; |
| | | private final String host; |
| | | private final int port; |
| | | |
| | | @SuppressWarnings("rawtypes") |
| | | private final Function<org.glassfish.grizzly.Connection, AbstractLdapConnectionImpl<?>, LdapException> |
| | | convertToLDAPConnection = |
| | | new Function<org.glassfish.grizzly.Connection, AbstractLdapConnectionImpl<?>, LdapException>() { |
| | | @Override |
| | | public GrizzlyLDAPConnection apply(org.glassfish.grizzly.Connection connection) throws LdapException { |
| | | configureConnection(connection, options.isTCPNoDelay(), options.isKeepAlive(), |
| | | options.isReuseAddress(), options.getLinger(), logger); |
| | | final GrizzlyLDAPConnection ldapConnection = |
| | | new GrizzlyLDAPConnection(connection, GrizzlyLDAPConnectionFactory.this); |
| | | timeoutChecker.get().addListener(ldapConnection); |
| | | clientFilter.registerConnection(connection, ldapConnection); |
| | | return ldapConnection; |
| | | } |
| | | }; |
| | | /** |
| | | * Prevents the transport and timeoutChecker being released when there are |
| | | * remaining references (this factory or any connections). It is initially |
| | | * set to 1 because this factory has a reference. |
| | | */ |
| | | private final AtomicInteger referenceCount = new AtomicInteger(1); |
| | | |
| | | /** |
| | | * Indicates whether this factory has been closed or not. |
| | | */ |
| | | private final AtomicBoolean isClosed = new AtomicBoolean(); |
| | | |
| | | private final ReferenceCountedObject<TCPNIOTransport>.Reference transport; |
| | | private final ReferenceCountedObject<TimeoutChecker>.Reference timeoutChecker = TIMEOUT_CHECKER |
| | | .acquire(); |
| | | |
| | | /** |
| | | * Creates a new LDAP connection factory based on Grizzly which can be used |
| | |
| | | */ |
| | | public GrizzlyLDAPConnectionFactory(final String host, final int port, final LDAPOptions options) { |
| | | this(host, port, options, null); |
| | | |
| | | } |
| | | |
| | | private LdapException adaptConnectionException(Throwable t) { |
| | | if (t instanceof LdapException) { |
| | | return (LdapException) t; |
| | | } |
| | | t = t instanceof ExecutionException && t.getCause() != null ? t.getCause() : t; |
| | | return newLdapException(ResultCode.CLIENT_SIDE_CONNECT_ERROR, t.getMessage(), t); |
| | | } |
| | | |
| | | /** |
| | |
| | | * connections. If {@code null}, default transport will be used. |
| | | */ |
| | | public GrizzlyLDAPConnectionFactory(final String host, final int port, final LDAPOptions options, |
| | | final TCPNIOTransport transport) { |
| | | super(host, port, options); |
| | | TCPNIOTransport transport) { |
| | | this.transport = DEFAULT_TRANSPORT.acquireIfNull(transport); |
| | | this.clientFilter = new LDAPClientFilter(options.getDecodeOptions(), 0); |
| | | this.defaultFilterChain = buildFilterChain(this.transport.get().getProcessor(), clientFilter); |
| | | this.host = host; |
| | | this.port = port; |
| | | this.options = new LDAPOptions(options); |
| | | this.clientFilter = new LDAPClientFilter(this.options.getDecodeOptions(), 0); |
| | | this.defaultFilterChain = |
| | | buildFilterChain(this.transport.get().getProcessor(), clientFilter); |
| | | } |
| | | |
| | | @Override |
| | | public void close() { |
| | | if (isClosed.compareAndSet(false, true)) { |
| | | releaseTransportAndTimeoutChecker(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public Connection getConnection() throws LdapException { |
| | | try { |
| | | return getConnectionAsync().getOrThrow(); |
| | | } catch (final InterruptedException e) { |
| | | throw newLdapException(ResultCode.CLIENT_SIDE_USER_CANCELLED, e); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public Promise<Connection, LdapException> getConnectionAsync() { |
| | | acquireTransportAndTimeoutChecker(); // Protect resources. |
| | | final SocketConnectorHandler connectorHandler = |
| | | TCPNIOConnectorHandler.builder(transport.get()).processor(defaultFilterChain) |
| | | .build(); |
| | | final PromiseImpl<Connection, LdapException> promise = PromiseImpl.create(); |
| | | connectorHandler.connect(getSocketAddress(), new CompletionHandlerAdapter(promise)); |
| | | return promise; |
| | | } |
| | | |
| | | @Override |
| | | public InetSocketAddress getSocketAddress() { |
| | | return new InetSocketAddress(host, port); |
| | | } |
| | | |
| | | @Override |
| | | public String getHostName() { |
| | | return host; |
| | | } |
| | | |
| | | @Override |
| | | public int getPort() { |
| | | return port; |
| | | } |
| | | |
| | | @Override |
| | | public String toString() { |
| | | return getClass().getSimpleName() + "(" + host + ':' + port + ')'; |
| | | } |
| | | |
| | | TimeoutChecker getTimeoutChecker() { |
| | | return timeoutChecker.get(); |
| | | } |
| | | |
| | | @Override |
| | | @SuppressWarnings("rawtypes") |
| | | protected Promise<AbstractLdapConnectionImpl<?>, LdapException> getConnectionAsync0() { |
| | | final SocketConnectorHandler connectorHandler = TCPNIOConnectorHandler.builder(transport.get()) |
| | | .processor(defaultFilterChain).build(); |
| | | final PromiseImpl<org.glassfish.grizzly.Connection, LdapException> promise = PromiseImpl.create(); |
| | | connectorHandler.connect(getSocketAddress(), new CompletionHandlerAdapter(promise)); |
| | | |
| | | return promise.then(convertToLDAPConnection); |
| | | LDAPOptions getLDAPOptions() { |
| | | return options; |
| | | } |
| | | |
| | | @Override |
| | | protected Promise<Void, LdapException> installSecureLayer(final Connection connection) { |
| | | final PromiseImpl<Void, LdapException> sslHandshakePromise = PromiseImpl.create(); |
| | | try { |
| | | final GrizzlyLDAPConnection grizzlyConnection = (GrizzlyLDAPConnection) connection; |
| | | grizzlyConnection.startTLS(options.getSSLContext(), options.getEnabledProtocols(), |
| | | options.getEnabledCipherSuites(), new EmptyCompletionHandler<SSLEngine>() { |
| | | @Override |
| | | public void completed(final SSLEngine result) { |
| | | if (!sslHandshakePromise.tryHandleResult(null)) { |
| | | // The connection has been either cancelled or |
| | | // it has timed out. |
| | | connection.close(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void failed(final Throwable throwable) { |
| | | sslHandshakePromise.handleError(adaptConnectionException(throwable)); |
| | | } |
| | | }); |
| | | } catch (final IOException e) { |
| | | sslHandshakePromise.handleError(adaptConnectionException(e)); |
| | | void releaseTransportAndTimeoutChecker() { |
| | | if (referenceCount.decrementAndGet() == 0) { |
| | | transport.release(); |
| | | timeoutChecker.release(); |
| | | } |
| | | |
| | | return sslHandshakePromise; |
| | | } |
| | | |
| | | @Override |
| | | protected void releaseImplResources() { |
| | | transport.release(); |
| | | timeoutChecker.release(); |
| | | private void acquireTransportAndTimeoutChecker() { |
| | | /* |
| | | * If the factory is not closed then we need to prevent the resources |
| | | * (transport, timeout checker) from being released while the connection |
| | | * attempt is in progress. |
| | | */ |
| | | referenceCount.incrementAndGet(); |
| | | if (isClosed.get()) { |
| | | releaseTransportAndTimeoutChecker(); |
| | | throw new IllegalStateException("Attempted to get a connection after factory close"); |
| | | } |
| | | } |
| | | |
| | | } |
| | |
| | | */ |
| | | final class LDAPClientFilter extends LDAPBaseFilter { |
| | | private static final Attribute<GrizzlyLDAPConnection> LDAP_CONNECTION_ATTR = |
| | | Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute("GrizzlyLdapClientConnection"); |
| | | Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute("LDAPClientConnection"); |
| | | |
| | | private static final Attribute<ClientResponseHandler> RESPONSE_HANDLER_ATTR = |
| | | Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute("ClientResponseHandler"); |
| | |
| | | * |
| | | * @return the reader to read incoming LDAP messages |
| | | */ |
| | | @Override |
| | | public LDAPReader<ASN1BufferReader> getReader() { |
| | | return this.reader; |
| | | } |
| | |
| | | IOException { |
| | | final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(context.getConnection()); |
| | | if (ldapConnection != null) { |
| | | final ResultLdapPromiseImpl pendingRequest = ldapConnection.removePendingResult(messageID); |
| | | final ResultLdapPromiseImpl pendingRequest = ldapConnection.removePendingRequest(messageID); |
| | | if (pendingRequest != null) { |
| | | if (pendingRequest.getRequest() instanceof AddRequest) { |
| | | pendingRequest.setResultOrError(result); |
| | |
| | | throws DecodeException, IOException { |
| | | final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(context.getConnection()); |
| | | if (ldapConnection != null) { |
| | | final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.removePendingResult(messageID); |
| | | final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.removePendingRequest(messageID); |
| | | if (pendingRequest != null) { |
| | | if (pendingRequest instanceof BindResultLdapPromiseImpl) { |
| | | final BindResultLdapPromiseImpl promise = (BindResultLdapPromiseImpl) pendingRequest; |
| | |
| | | |
| | | try { |
| | | if (!bindClient.evaluateResult(result)) { |
| | | // The server is expecting a multi stage bind response. |
| | | // The server is expecting a multi stage |
| | | // bind response. |
| | | final int msgID = ldapConnection.continuePendingBindRequest(promise); |
| | | |
| | | LDAPWriter<ASN1BufferWriter> ldapWriter = GrizzlyUtils.getWriter(); |
| | |
| | | throws DecodeException, IOException { |
| | | final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(context.getConnection()); |
| | | if (ldapConnection != null) { |
| | | final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.removePendingResult(messageID); |
| | | final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.removePendingRequest(messageID); |
| | | if (pendingRequest != null) { |
| | | if (pendingRequest.getRequest() instanceof CompareRequest) { |
| | | ((ResultLdapPromiseImpl<CompareRequest, CompareResult>) pendingRequest) |
| | |
| | | IOException { |
| | | final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(context.getConnection()); |
| | | if (ldapConnection != null) { |
| | | final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.removePendingResult(messageID); |
| | | final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.removePendingRequest(messageID); |
| | | if (pendingRequest != null) { |
| | | if (pendingRequest.getRequest() instanceof DeleteRequest) { |
| | | ((ResultLdapPromiseImpl<DeleteRequest, Result>) pendingRequest).setResultOrError(result); |
| | |
| | | // Treat this as a connection error. |
| | | final Result errorResult = newResult(result.getResultCode()).setDiagnosticMessage( |
| | | result.getDiagnosticMessage()); |
| | | ldapConnection.connectionErrorOccurred(true, errorResult); |
| | | ldapConnection.close(null, true, errorResult); |
| | | } else { |
| | | ldapConnection.handleUnsolicitedNotification(result); |
| | | } |
| | | } else { |
| | | final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.removePendingResult(messageID); |
| | | final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.removePendingRequest(messageID); |
| | | if (pendingRequest != null) { |
| | | if (pendingRequest.getRequest() instanceof ExtendedRequest) { |
| | | final ExtendedResultLdapPromiseImpl<?> extendedPromise = |
| | |
| | | throws DecodeException, IOException { |
| | | final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(context.getConnection()); |
| | | if (ldapConnection != null) { |
| | | final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.getPendingResult(messageID); |
| | | final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.getPendingRequest(messageID); |
| | | if (pendingRequest != null) { |
| | | pendingRequest.handleIntermediateResponse(response); |
| | | } |
| | |
| | | throws DecodeException, IOException { |
| | | final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(context.getConnection()); |
| | | if (ldapConnection != null) { |
| | | final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.removePendingResult(messageID); |
| | | final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.removePendingRequest(messageID); |
| | | if (pendingRequest != null) { |
| | | if (pendingRequest.getRequest() instanceof ModifyDNRequest) { |
| | | ((ResultLdapPromiseImpl<ModifyDNRequest, Result>) pendingRequest).setResultOrError(result); |
| | |
| | | public void modifyResult(final int messageID, final Result result) throws DecodeException, IOException { |
| | | final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(context.getConnection()); |
| | | if (ldapConnection != null) { |
| | | final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.removePendingResult(messageID); |
| | | final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.removePendingRequest(messageID); |
| | | if (pendingRequest != null) { |
| | | if (pendingRequest.getRequest() instanceof ModifyRequest) { |
| | | ((ResultLdapPromiseImpl<ModifyRequest, Result>) pendingRequest).setResultOrError(result); |
| | |
| | | IOException { |
| | | final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(context.getConnection()); |
| | | if (ldapConnection != null) { |
| | | final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.removePendingResult(messageID); |
| | | final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.removePendingRequest(messageID); |
| | | if (pendingRequest != null) { |
| | | if (pendingRequest.getRequest() instanceof SearchRequest) { |
| | | ((ResultLdapPromiseImpl<SearchRequest, Result>) pendingRequest).setResultOrError(result); |
| | |
| | | throws DecodeException, IOException { |
| | | final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(context.getConnection()); |
| | | if (ldapConnection != null) { |
| | | final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.getPendingResult(messageID); |
| | | final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.getPendingRequest(messageID); |
| | | if (pendingRequest != null) { |
| | | if (pendingRequest instanceof SearchResultLdapPromiseImpl) { |
| | | ((SearchResultLdapPromiseImpl) pendingRequest).handleEntry(entry); |
| | |
| | | throws DecodeException, IOException { |
| | | final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(context.getConnection()); |
| | | if (ldapConnection != null) { |
| | | final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.getPendingResult(messageID); |
| | | final ResultLdapPromiseImpl<?, ?> pendingRequest = ldapConnection.getPendingRequest(messageID); |
| | | if (pendingRequest != null) { |
| | | if (pendingRequest instanceof SearchResultLdapPromiseImpl) { |
| | | ((SearchResultLdapPromiseImpl) pendingRequest).handleReference(reference); |
| | |
| | | private <R extends ExtendedResult> void handleExtendedResult0( |
| | | final GrizzlyLDAPConnection conn, final ExtendedResultLdapPromiseImpl<R> promise, |
| | | final ExtendedResult result) throws DecodeException { |
| | | final R decodedResponse = promise.decodeResult(result, conn.getLdapOptions().getDecodeOptions()); |
| | | final R decodedResponse = promise.decodeResult(result, conn.getLDAPOptions().getDecodeOptions()); |
| | | |
| | | if (result.getResultCode() == ResultCode.SUCCESS |
| | | && promise.getRequest() instanceof StartTLSExtendedRequest) { |
| | | try { |
| | | final StartTLSExtendedRequest request = (StartTLSExtendedRequest) promise.getRequest(); |
| | | conn.startTLS(request.getSSLContext(), request.getEnabledProtocols(), |
| | | request.getEnabledCipherSuites(), new EmptyCompletionHandler<SSLEngine>() { |
| | | request.getEnabledCipherSuites(), |
| | | new EmptyCompletionHandler<SSLEngine>() { |
| | | @Override |
| | | public void completed(final SSLEngine result) { |
| | | conn.setBindOrStartTLSInProgress(false); |
| | |
| | | |
| | | @Override |
| | | public void failed(final Throwable throwable) { |
| | | final Result errorResult = newResult(CLIENT_SIDE_LOCAL_ERROR).setCause(throwable) |
| | | .setDiagnosticMessage("SSL handshake failed"); |
| | | final Result errorResult = newResult(CLIENT_SIDE_LOCAL_ERROR) |
| | | .setCause(throwable).setDiagnosticMessage("SSL handshake failed"); |
| | | conn.setBindOrStartTLSInProgress(false); |
| | | conn.connectionErrorOccurred(false, errorResult); |
| | | conn.close(null, false, errorResult); |
| | | promise.adaptErrorResult(errorResult); |
| | | } |
| | | }); |
| | | return; |
| | | } catch (final IOException e) { |
| | | final Result errorResult = newResult(CLIENT_SIDE_LOCAL_ERROR).setCause(e).setDiagnosticMessage( |
| | | e.getMessage()); |
| | | final Result errorResult = newResult(CLIENT_SIDE_LOCAL_ERROR).setCause(e) |
| | | .setDiagnosticMessage(e.getMessage()); |
| | | promise.adaptErrorResult(errorResult); |
| | | conn.connectionErrorOccurred(false, errorResult); |
| | | conn.close(null, false, errorResult); |
| | | return; |
| | | } |
| | | } |
| | |
| | | } |
| | | final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(connection); |
| | | |
| | | // FIXME: what other sort of IOExceptions can be thrown? |
| | | // FIXME: Are they the best result codes? |
| | | Result errorResult = newResult(error instanceof EOFException ? ResultCode.CLIENT_SIDE_SERVER_DOWN |
| | | : ResultCode.CLIENT_SIDE_LOCAL_ERROR); |
| | | ldapConnection.connectionErrorOccurred(false, errorResult.setCause(error)); |
| | | Result errorResult; |
| | | if (error instanceof EOFException) { |
| | | // FIXME: Is this the best result code? |
| | | errorResult = Responses.newResult(ResultCode.CLIENT_SIDE_SERVER_DOWN).setCause(error); |
| | | } else { |
| | | // FIXME: what other sort of IOExceptions can be thrown? |
| | | // FIXME: Is this the best result code? |
| | | errorResult = Responses.newResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR).setCause(error); |
| | | } |
| | | ldapConnection.close(null, false, errorResult); |
| | | } |
| | | |
| | | @Override |
| | |
| | | final Connection<?> connection = ctx.getConnection(); |
| | | final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.remove(connection); |
| | | if (ldapConnection != null) { |
| | | ldapConnection.connectionErrorOccurred(false, newResult(ResultCode.CLIENT_SIDE_SERVER_DOWN)); |
| | | final Result errorResult = Responses.newResult(ResultCode.CLIENT_SIDE_SERVER_DOWN); |
| | | ldapConnection.close(null, false, errorResult); |
| | | } |
| | | return ctx.getInvokeAction(); |
| | | } |
| | |
| | | final Result errorResult = |
| | | Responses.newResult(ResultCode.CLIENT_SIDE_DECODING_ERROR).setCause(e) |
| | | .setDiagnosticMessage(e.getMessage()); |
| | | ldapConnection.connectionErrorOccurred(false, errorResult); |
| | | ldapConnection.close(null, false, errorResult); |
| | | } |
| | | |
| | | /** |
| | |
| | | * @param ldapConnection |
| | | * LDAP connection |
| | | */ |
| | | void registerConnection(final Connection<?> connection, final GrizzlyLDAPConnection ldapConnection) { |
| | | void registerConnection(final Connection<?> connection, |
| | | final GrizzlyLDAPConnection ldapConnection) { |
| | | LDAP_CONNECTION_ATTR.set(connection, ldapConnection); |
| | | } |
| | | } |
| | |
| | | # |
| | | # 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 |
| | | LDAP_CONNECTION_BIND_OR_START_TLS_CONNECTION_TIMEOUT=The LDAP connection has \ |
| | | failed because no bind or StartTLS response was received from the server \ |
| | | within the %d ms timeout |
| | |
| | | import org.forgerock.opendj.ldap.ConnectionPool; |
| | | import org.forgerock.opendj.ldap.Connections; |
| | | import org.forgerock.opendj.ldap.DN; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.FailoverLoadBalancingAlgorithm; |
| | | import org.forgerock.opendj.ldap.LdapPromise; |
| | | import org.forgerock.opendj.ldap.IntermediateResponseHandler; |
| | | import org.forgerock.opendj.ldap.LDAPClientContext; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.LDAPListener; |
| | | import org.forgerock.opendj.ldap.LDAPOptions; |
| | | import org.forgerock.opendj.ldap.LDAPServer; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.LdapPromise; |
| | | import org.forgerock.opendj.ldap.MockConnectionEventListener; |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.ResultHandler; |
| | |
| | | import org.testng.annotations.DataProvider; |
| | | import org.testng.annotations.Test; |
| | | |
| | | import static java.util.concurrent.TimeUnit.*; |
| | | |
| | | import static org.fest.assertions.Assertions.*; |
| | | import static org.forgerock.opendj.ldap.Connections.*; |
| | | import static org.forgerock.opendj.ldap.LdapException.*; |
| | | import static org.forgerock.opendj.ldap.TestCaseUtils.*; |
| | | import static org.forgerock.opendj.ldap.requests.Requests.*; |
| | | import static org.forgerock.opendj.ldap.responses.Responses.*; |
| | | import static org.forgerock.opendj.ldap.spi.LdapPromises.*; |
| | | import static org.mockito.Matchers.*; |
| | | import static org.mockito.Mockito.*; |
| | |
| | | private static final long TEST_TIMEOUT = 30L; |
| | | private static final long TEST_TIMEOUT_MS = TEST_TIMEOUT * 1000L; |
| | | |
| | | /** The heart-beat timeout after which a connection will be marked as failed. */ |
| | | private static final long OPERATION_TIMEOUT_SEC = 3; |
| | | |
| | | /** The time unit for the interval between keepalive pings. */ |
| | | private static final long HEART_BEAT_INTERVAL_SEC = 10; |
| | | |
| | | /** |
| | | * Ensures that the LDAP Server is running. |
| | | * |
| | |
| | | "uid=user.0,ou=people,o=test", SearchScope.BASE_OBJECT, "(objectclass=*)", "cn"); |
| | | |
| | | InetSocketAddress serverAddress = getServerSocketAddress(); |
| | | final LDAPOptions commonsOptions = new LDAPOptions().setTimeout(OPERATION_TIMEOUT_SEC, SECONDS); |
| | | factories[0][0] = newLDAPConnectionFactory(serverAddress.getHostName(), serverAddress.getPort(), |
| | | new LDAPOptions().setHeartBeatSearchRequest(request).setTimeout(500, MILLISECONDS) |
| | | .setHeartBeatInterval(1000, MILLISECONDS)); |
| | | factories[0][0] = |
| | | Connections.newHeartBeatConnectionFactory(new LDAPConnectionFactory( |
| | | serverAddress.getHostName(), serverAddress.getPort()), |
| | | 1000, 500, TimeUnit.MILLISECONDS, request); |
| | | |
| | | // InternalConnectionFactory |
| | | factories[1][0] = newInternalConnectionFactory(LDAPServer.getInstance(), null); |
| | | factories[1][0] = Connections.newInternalConnectionFactory(LDAPServer.getInstance(), null); |
| | | |
| | | // AuthenticatedConnectionFactory |
| | | factories[2][0] = newLDAPConnectionFactory(serverAddress.getHostName(), serverAddress.getPort(), |
| | | new LDAPOptions(commonsOptions).setBindRequest(newSimpleBindRequest("", new char[0]))); |
| | | factories[2][0] = |
| | | Connections.newAuthenticatedConnectionFactory(new LDAPConnectionFactory( |
| | | serverAddress.getHostName(), serverAddress.getPort()), |
| | | Requests.newSimpleBindRequest("", new char[0])); |
| | | |
| | | // AuthenticatedConnectionFactory with multi-stage SASL |
| | | factories[3][0] = newLDAPConnectionFactory(serverAddress.getHostName(), serverAddress.getPort(), |
| | | new LDAPOptions(commonsOptions).setBindRequest( |
| | | newCRAMMD5SASLBindRequest("id:user", "password".toCharArray()))); |
| | | factories[3][0] = |
| | | Connections.newAuthenticatedConnectionFactory(new LDAPConnectionFactory( |
| | | serverAddress.getHostName(), serverAddress.getPort()), |
| | | Requests.newCRAMMD5SASLBindRequest("id:user", "password".toCharArray())); |
| | | |
| | | // LDAPConnectionFactory with default options |
| | | factories[4][0] = newLDAPConnectionFactory(serverAddress.getHostName(), serverAddress.getPort()); |
| | | factories[4][0] = new LDAPConnectionFactory(serverAddress.getHostName(), serverAddress.getPort()); |
| | | |
| | | // LDAPConnectionFactory with startTLS |
| | | SSLContext sslContext = |
| | | new SSLContextBuilder().setTrustManager(TrustManagers.trustAll()).getSSLContext(); |
| | | LDAPOptions options = new LDAPOptions(commonsOptions).setSSLContext(sslContext).setUseStartTLS(true) |
| | | LDAPOptions options = new LDAPOptions().setSSLContext(sslContext).setUseStartTLS(true) |
| | | .addEnabledCipherSuite( |
| | | new String[] { "SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA", |
| | | "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5", |
| | |
| | | "SSL_DH_anon_WITH_DES_CBC_SHA", "SSL_DH_anon_WITH_RC4_128_MD5", |
| | | "TLS_DH_anon_WITH_AES_128_CBC_SHA", |
| | | "TLS_DH_anon_WITH_AES_256_CBC_SHA" }); |
| | | factories[5][0] = newLDAPConnectionFactory(serverAddress.getHostName(), serverAddress.getPort(), options); |
| | | factories[5][0] = new LDAPConnectionFactory(serverAddress.getHostName(), serverAddress.getPort(), options); |
| | | |
| | | // startTLS + SASL confidentiality |
| | | // Use IP address here so that DIGEST-MD5 host verification works if |
| | | // local host name is not localhost (e.g. on some machines it might be |
| | | // localhost.localdomain). |
| | | // FIXME: enable QOP once OPENDJ-514 is fixed. |
| | | factories[6][0] = newLDAPConnectionFactory(serverAddress.getHostName(), serverAddress.getPort(), |
| | | options.setBindRequest(newDigestMD5SASLBindRequest("id:user", "password".toCharArray()) |
| | | .setCipher(DigestMD5SASLBindRequest.CIPHER_LOW))); |
| | | factories[6][0] = |
| | | Connections.newAuthenticatedConnectionFactory(new LDAPConnectionFactory( |
| | | serverAddress.getHostName(), serverAddress.getPort(), options), |
| | | Requests.newDigestMD5SASLBindRequest( |
| | | "id:user", "password".toCharArray()).setCipher( |
| | | DigestMD5SASLBindRequest.CIPHER_LOW)); |
| | | |
| | | // Connection pool and load balancing tests. |
| | | InetSocketAddress offlineSocketAddress1 = findFreeSocketAddress(); |
| | | ConnectionFactory offlineServer1 = newNamedConnectionFactory( |
| | | newLDAPConnectionFactory(offlineSocketAddress1.getHostName(), |
| | | ConnectionFactory offlineServer1 = |
| | | Connections.newNamedConnectionFactory( |
| | | new LDAPConnectionFactory(offlineSocketAddress1.getHostName(), |
| | | offlineSocketAddress1.getPort()), "offline1"); |
| | | |
| | | InetSocketAddress offlineSocketAddress2 = findFreeSocketAddress(); |
| | | ConnectionFactory offlineServer2 = newNamedConnectionFactory( |
| | | newLDAPConnectionFactory(offlineSocketAddress2.getHostName(), |
| | | ConnectionFactory offlineServer2 = |
| | | Connections.newNamedConnectionFactory( |
| | | new LDAPConnectionFactory(offlineSocketAddress2.getHostName(), |
| | | offlineSocketAddress2.getPort()), "offline2"); |
| | | ConnectionFactory onlineServer = newNamedConnectionFactory( |
| | | newLDAPConnectionFactory(serverAddress.getHostName(), |
| | | ConnectionFactory onlineServer = |
| | | Connections.newNamedConnectionFactory( |
| | | new LDAPConnectionFactory(serverAddress.getHostName(), |
| | | serverAddress.getPort()), "online"); |
| | | |
| | | // Connection pools. |
| | |
| | | // Create a connection factory: this should always use the default |
| | | // schema, even if it is updated. |
| | | InetSocketAddress socketAddress = getServerSocketAddress(); |
| | | final ConnectionFactory factory = newLDAPConnectionFactory(socketAddress.getHostName(), |
| | | final ConnectionFactory factory = new LDAPConnectionFactory(socketAddress.getHostName(), |
| | | socketAddress.getPort()); |
| | | final Schema defaultSchema = Schema.getDefaultSchema(); |
| | | |
| | |
| | | @Test(dataProvider = "closeNotifyConfig") |
| | | public void testCloseNotify(final CloseNotify config) throws Exception { |
| | | final CountDownLatch connectLatch = new CountDownLatch(1); |
| | | final AtomicReference<LDAPClientContext> contextHolder = new AtomicReference<LDAPClientContext>(); |
| | | final AtomicReference<LDAPClientContext> contextHolder = |
| | | new AtomicReference<LDAPClientContext>(); |
| | | |
| | | final ServerConnectionFactory<LDAPClientContext, Integer> mockServer = mock(ServerConnectionFactory.class); |
| | | final ServerConnectionFactory<LDAPClientContext, Integer> mockServer = |
| | | mock(ServerConnectionFactory.class); |
| | | when(mockServer.handleAccept(any(LDAPClientContext.class))).thenAnswer( |
| | | new Answer<ServerConnection<Integer>>() { |
| | | |
| | | @Override |
| | | public ServerConnection<Integer> answer(InvocationOnMock invocation) throws Throwable { |
| | | public ServerConnection<Integer> answer(InvocationOnMock invocation) |
| | | throws Throwable { |
| | | // Allow the context to be accessed from outside the mock. |
| | | contextHolder.set((LDAPClientContext) invocation.getArguments()[0]); |
| | | connectLatch.countDown(); /* is this needed? */ |
| | |
| | | @Override |
| | | public Void answer(InvocationOnMock invocation) throws Throwable { |
| | | ResultHandler<? super BindResult> resultHandler = |
| | | (ResultHandler<? super BindResult>) invocation.getArguments()[4]; |
| | | resultHandler.handleResult(newBindResult(ResultCode.SUCCESS)); |
| | | (ResultHandler<? super BindResult>) invocation |
| | | .getArguments()[4]; |
| | | resultHandler.handleResult(Responses |
| | | .newBindResult(ResultCode.SUCCESS)); |
| | | return null; |
| | | } |
| | | }).when(mockConnection).handleBind(anyInt(), anyInt(), |
| | |
| | | } |
| | | }); |
| | | |
| | | LDAPListener listener = newLDAPListener(findFreeSocketAddress(), mockServer); |
| | | LDAPListener listener = new LDAPListener(findFreeSocketAddress(), mockServer); |
| | | try { |
| | | LDAPConnectionFactory clientFactory = newLDAPConnectionFactory(listener.getHostName(), listener.getPort()); |
| | | final Connection clientConnection = clientFactory.getConnection(); |
| | | connectLatch.await(TEST_TIMEOUT, SECONDS); |
| | | LDAPConnectionFactory clientFactory = |
| | | new LDAPConnectionFactory(listener.getHostName(), listener.getPort()); |
| | | final Connection client = clientFactory.getConnection(); |
| | | connectLatch.await(TEST_TIMEOUT, TimeUnit.SECONDS); |
| | | MockConnectionEventListener mockListener = null; |
| | | try { |
| | | if (config.useEventListener) { |
| | | mockListener = new MockConnectionEventListener(); |
| | | clientConnection.addConnectionEventListener(mockListener); |
| | | client.addConnectionEventListener(mockListener); |
| | | } |
| | | if (config.doBindFirst) { |
| | | clientConnection.bind("cn=test", "password".toCharArray()); |
| | | client.bind("cn=test", "password".toCharArray()); |
| | | } |
| | | if (!config.closeOnAccept) { |
| | | // Disconnect using client context. |
| | |
| | | // Block until remote close is signalled. |
| | | if (mockListener != null) { |
| | | // Block using listener. |
| | | mockListener.awaitError(TEST_TIMEOUT, SECONDS); |
| | | mockListener.awaitError(TEST_TIMEOUT, TimeUnit.SECONDS); |
| | | assertThat(mockListener.getInvocationCount()).isEqualTo(1); |
| | | assertThat(mockListener.isDisconnectNotification()).isEqualTo(config.sendDisconnectNotification); |
| | | assertThat(mockListener.isDisconnectNotification()).isEqualTo( |
| | | config.sendDisconnectNotification); |
| | | assertThat(mockListener.getError()).isNotNull(); |
| | | } else { |
| | | // Block by spinning on isValid. |
| | | waitForCondition(new Callable<Boolean>() { |
| | | @Override |
| | | public Boolean call() throws Exception { |
| | | return !clientConnection.isValid(); |
| | | return !client.isValid(); |
| | | } |
| | | }); |
| | | } |
| | | assertThat(clientConnection.isValid()).isFalse(); |
| | | assertThat(clientConnection.isClosed()).isFalse(); |
| | | assertThat(client.isValid()).isFalse(); |
| | | assertThat(client.isClosed()).isFalse(); |
| | | } finally { |
| | | clientConnection.close(); |
| | | client.close(); |
| | | } |
| | | // Check state after remote close and local close. |
| | | assertThat(clientConnection.isValid()).isFalse(); |
| | | assertThat(clientConnection.isClosed()).isTrue(); |
| | | assertThat(client.isValid()).isFalse(); |
| | | assertThat(client.isClosed()).isTrue(); |
| | | if (mockListener != null) { |
| | | mockListener.awaitClose(TEST_TIMEOUT, SECONDS); |
| | | mockListener.awaitClose(TEST_TIMEOUT, TimeUnit.SECONDS); |
| | | assertThat(mockListener.getInvocationCount()).isEqualTo(2); |
| | | } |
| | | } finally { |
| | |
| | | } |
| | | }); |
| | | |
| | | LDAPListener listener = newLDAPListener(findFreeSocketAddress(), mockServer); |
| | | LDAPListener listener = new LDAPListener(findFreeSocketAddress(), mockServer); |
| | | try { |
| | | LDAPConnectionFactory clientFactory = newLDAPConnectionFactory(listener.getHostName(), listener.getPort()); |
| | | LDAPConnectionFactory clientFactory = |
| | | new LDAPConnectionFactory(listener.getHostName(), |
| | | listener.getPort()); |
| | | final Connection client = clientFactory.getConnection(); |
| | | connectLatch.await(TEST_TIMEOUT, TimeUnit.SECONDS); |
| | | try { |
| | |
| | | @Test(description = "Test for OPENDJ-1121: Closing a connection after " |
| | | + "closing the connection factory causes NPE") |
| | | public void testFactoryCloseBeforeConnectionClose() throws Exception { |
| | | LDAPOptions heartBeatOptions = new LDAPOptions().setHeartBeatInterval(HEART_BEAT_INTERVAL_SEC, SECONDS) |
| | | .setTimeout(OPERATION_TIMEOUT_SEC, SECONDS); |
| | | InetSocketAddress socketAddress = getServerSocketAddress(); |
| | | final ConnectionFactory factory = newLoadBalancer(new FailoverLoadBalancingAlgorithm( |
| | | Arrays.asList(newFixedConnectionPool(newLDAPConnectionFactory( |
| | | socketAddress.getHostName(), socketAddress.getPort(), heartBeatOptions), 2)))); |
| | | final ConnectionFactory factory = |
| | | newLoadBalancer(new FailoverLoadBalancingAlgorithm(Arrays.asList(newFixedConnectionPool( |
| | | newHeartBeatConnectionFactory(new LDAPConnectionFactory( |
| | | socketAddress.getHostName(), socketAddress.getPort())), 2)))); |
| | | Connection conn = null; |
| | | try { |
| | | conn = factory.getConnection(); |
| | |
| | | import org.forgerock.opendj.ldap.ConnectionFactory; |
| | | import org.forgerock.opendj.ldap.Connections; |
| | | import org.forgerock.opendj.ldap.DN; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.LdapPromise; |
| | | import org.forgerock.opendj.ldap.IntermediateResponseHandler; |
| | | import org.forgerock.opendj.ldap.LDAPClientContext; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.LDAPListener; |
| | | import org.forgerock.opendj.ldap.LDAPOptions; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.LdapPromise; |
| | | import org.forgerock.opendj.ldap.MockConnectionEventListener; |
| | | import org.forgerock.opendj.ldap.ProviderNotFoundException; |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | |
| | | |
| | | import static org.fest.assertions.Assertions.*; |
| | | import static org.fest.assertions.Fail.*; |
| | | import static org.forgerock.opendj.ldap.Connections.*; |
| | | import static org.forgerock.opendj.ldap.TestCaseUtils.*; |
| | | import static org.forgerock.opendj.ldap.requests.Requests.*; |
| | | import static org.mockito.Matchers.*; |
| | |
| | | new AtomicReference<LDAPClientContext>(); |
| | | private final LDAPListener server = createServer(); |
| | | private final InetSocketAddress socketAddress = server.getSocketAddress(); |
| | | private final ConnectionFactory factory = newLDAPConnectionFactory(socketAddress.getHostName(), |
| | | private final ConnectionFactory factory = new LDAPConnectionFactory(socketAddress.getHostName(), |
| | | socketAddress.getPort(), new LDAPOptions().setTimeout(1, TimeUnit.MILLISECONDS)); |
| | | private final ConnectionFactory pool = Connections.newFixedConnectionPool(factory, 10); |
| | | private volatile ServerConnection<Integer> serverConnection; |
| | |
| | | @Test(description = "OPENDJ-1197") |
| | | public void testClientSideConnectTimeout() throws Exception { |
| | | // Use an non-local unreachable network address. |
| | | final ConnectionFactory factory = newLDAPConnectionFactory("10.20.30.40", 1389, |
| | | final ConnectionFactory factory = new LDAPConnectionFactory("10.20.30.40", 1389, |
| | | new LDAPOptions().setConnectTimeout(1, TimeUnit.MILLISECONDS)); |
| | | try { |
| | | for (int i = 0; i < ITERATIONS; i++) { |
| | |
| | | public void testCreateLDAPConnectionFactory() throws Exception { |
| | | // test no exception is thrown, which means transport provider is correctly loaded |
| | | InetSocketAddress socketAddress = findFreeSocketAddress(); |
| | | LDAPConnectionFactory factory = newLDAPConnectionFactory(socketAddress.getHostName(), socketAddress.getPort()); |
| | | LDAPConnectionFactory factory = new LDAPConnectionFactory(socketAddress.getHostName(), socketAddress.getPort()); |
| | | factory.close(); |
| | | } |
| | | |
| | |
| | | public void testCreateLDAPConnectionFactoryFailureProviderNotFound() throws Exception { |
| | | LDAPOptions options = new LDAPOptions().setTransportProvider("unknown"); |
| | | InetSocketAddress socketAddress = findFreeSocketAddress(); |
| | | LDAPConnectionFactory factory = newLDAPConnectionFactory(socketAddress.getHostName(), |
| | | LDAPConnectionFactory factory = new LDAPConnectionFactory(socketAddress.getHostName(), |
| | | socketAddress.getPort(), options); |
| | | factory.close(); |
| | | } |
| | |
| | | // test no exception is thrown, which means transport provider is correctly loaded |
| | | LDAPOptions options = new LDAPOptions().setProviderClassLoader(Thread.currentThread().getContextClassLoader()); |
| | | InetSocketAddress socketAddress = findFreeSocketAddress(); |
| | | LDAPConnectionFactory factory = newLDAPConnectionFactory(socketAddress.getHostName(), |
| | | LDAPConnectionFactory factory = new LDAPConnectionFactory(socketAddress.getHostName(), |
| | | socketAddress.getPort(), options); |
| | | factory.close(); |
| | | } |
| | |
| | | |
| | | private LDAPListener createServer() { |
| | | try { |
| | | return newLDAPListener(findFreeSocketAddress(), |
| | | return new LDAPListener(findFreeSocketAddress(), |
| | | new ServerConnectionFactory<LDAPClientContext, Integer>() { |
| | | @Override |
| | | public ServerConnection<Integer> handleAccept( |
| | |
| | | |
| | | package org.forgerock.opendj.grizzly; |
| | | |
| | | import static org.fest.assertions.Assertions.assertThat; |
| | | import static org.mockito.Mockito.mock; |
| | | import static org.mockito.Mockito.verify; |
| | | import static org.mockito.Mockito.verifyZeroInteractions; |
| | | |
| | | import java.net.InetSocketAddress; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | import org.forgerock.opendj.ldap.Connections; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.LDAPListener; |
| | | import org.forgerock.opendj.ldap.LDAPOptions; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.RequestHandler; |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.SdkTestCase; |
| | |
| | | import org.mockito.ArgumentCaptor; |
| | | import org.testng.annotations.Test; |
| | | |
| | | import static org.fest.assertions.Assertions.*; |
| | | import static org.forgerock.opendj.ldap.Connections.*; |
| | | import static org.mockito.Mockito.*; |
| | | |
| | | /** |
| | | * Tests LDAP connection implementation class. |
| | | */ |
| | |
| | | * and leave the client waiting forever for a response. |
| | | */ |
| | | @SuppressWarnings("unchecked") |
| | | LDAPListener listener = newLDAPListener(address, newServerConnectionFactory(mock(RequestHandler.class))); |
| | | LDAPListener listener = |
| | | new LDAPListener(address, Connections |
| | | .newServerConnectionFactory(mock(RequestHandler.class))); |
| | | |
| | | /* |
| | | * Use a very long time out in order to prevent the timeout thread from |
| | | * triggering the timeout. |
| | | */ |
| | | LDAPConnectionFactory factory = newLDAPConnectionFactory(address.getHostName(), |
| | | LDAPConnectionFactory factory = new LDAPConnectionFactory(address.getHostName(), |
| | | address.getPort(), new LDAPOptions().setTimeout(100, TimeUnit.SECONDS)); |
| | | GrizzlyLDAPConnection connection = (GrizzlyLDAPConnection) factory.getConnection(); |
| | | try { |
| | |
| | | |
| | | import static org.fest.assertions.Assertions.*; |
| | | import static org.fest.assertions.Fail.*; |
| | | import static org.forgerock.opendj.ldap.Connections.*; |
| | | import static org.forgerock.opendj.ldap.LdapException.*; |
| | | import static org.forgerock.opendj.ldap.TestCaseUtils.*; |
| | | import static org.mockito.Mockito.*; |
| | |
| | | public void testCreateLDAPListener() throws Exception { |
| | | // test no exception is thrown, which means transport provider is |
| | | // correctly loaded |
| | | LDAPListener listener = newLDAPListener(findFreeSocketAddress(), mock(ServerConnectionFactory.class)); |
| | | LDAPListener listener = new LDAPListener(findFreeSocketAddress(), mock(ServerConnectionFactory.class)); |
| | | listener.close(); |
| | | } |
| | | |
| | |
| | | // correctly loaded |
| | | LDAPListenerOptions options = new LDAPListenerOptions(). |
| | | setProviderClassLoader(Thread.currentThread().getContextClassLoader()); |
| | | LDAPListener listener = newLDAPListener(findFreeSocketAddress(), |
| | | LDAPListener listener = new LDAPListener(findFreeSocketAddress(), |
| | | mock(ServerConnectionFactory.class), options); |
| | | listener.close(); |
| | | } |
| | |
| | | expectedExceptionsMessageRegExp = "^The requested provider 'unknown' .*") |
| | | public void testCreateLDAPListenerFailureProviderNotFound() throws Exception { |
| | | LDAPListenerOptions options = new LDAPListenerOptions().setTransportProvider("unknown"); |
| | | LDAPListener listener = newLDAPListener(findFreeSocketAddress(), mock(ServerConnectionFactory.class), options); |
| | | LDAPListener listener = new LDAPListener(findFreeSocketAddress(), mock(ServerConnectionFactory.class), options); |
| | | listener.close(); |
| | | } |
| | | |
| | |
| | | final MockServerConnection serverConnection = new MockServerConnection(); |
| | | final MockServerConnectionFactory serverConnectionFactory = |
| | | new MockServerConnectionFactory(serverConnection); |
| | | final LDAPListener listener = newLDAPListener(new InetSocketAddress(0), serverConnectionFactory); |
| | | final LDAPListener listener = |
| | | new LDAPListener(new InetSocketAddress(0), serverConnectionFactory); |
| | | try { |
| | | // Connect and close. |
| | | final Connection connection = newLDAPConnectionFactory(listener.getHostName(), listener.getPort()) |
| | | .getConnection(); |
| | | final Connection connection = |
| | | new LDAPConnectionFactory(listener.getHostName(), |
| | | listener.getPort()).getConnection(); |
| | | assertThat(serverConnection.context.get(10, TimeUnit.SECONDS)).isNotNull(); |
| | | assertThat(serverConnection.isClosed.getCount()).isEqualTo(1); |
| | | connection.close(); |
| | |
| | | final MockServerConnectionFactory onlineServerConnectionFactory = |
| | | new MockServerConnectionFactory(onlineServerConnection); |
| | | final LDAPListener onlineServerListener = |
| | | newLDAPListener(findFreeSocketAddress(), onlineServerConnectionFactory); |
| | | new LDAPListener(findFreeSocketAddress(), onlineServerConnectionFactory); |
| | | |
| | | try { |
| | | // Connection pool and load balancing tests. |
| | | InetSocketAddress offlineAddress1 = findFreeSocketAddress(); |
| | | final ConnectionFactory offlineServer1 = |
| | | newNamedConnectionFactory(newLDAPConnectionFactory( |
| | | Connections.newNamedConnectionFactory(new LDAPConnectionFactory( |
| | | offlineAddress1.getHostName(), |
| | | offlineAddress1.getPort()), "offline1"); |
| | | InetSocketAddress offlineAddress2 = findFreeSocketAddress(); |
| | | final ConnectionFactory offlineServer2 = |
| | | newNamedConnectionFactory(newLDAPConnectionFactory( |
| | | Connections.newNamedConnectionFactory(new LDAPConnectionFactory( |
| | | offlineAddress2.getHostName(), |
| | | offlineAddress2.getPort()), "offline2"); |
| | | final ConnectionFactory onlineServer = |
| | | newNamedConnectionFactory(newLDAPConnectionFactory( |
| | | Connections.newNamedConnectionFactory(new LDAPConnectionFactory( |
| | | onlineServerListener.getHostName(), |
| | | onlineServerListener.getPort()), "online"); |
| | | |
| | |
| | | |
| | | }; |
| | | |
| | | final LDAPListener proxyListener = newLDAPListener(findFreeSocketAddress(), proxyServerConnectionFactory); |
| | | final LDAPListener proxyListener = |
| | | new LDAPListener(findFreeSocketAddress(), proxyServerConnectionFactory); |
| | | try { |
| | | // Connect and close. |
| | | final Connection connection = newLDAPConnectionFactory( |
| | | proxyListener.getHostName(), proxyListener.getPort()).getConnection(); |
| | | final Connection connection = |
| | | new LDAPConnectionFactory(proxyListener.getHostName(), |
| | | proxyListener.getPort()).getConnection(); |
| | | |
| | | assertThat(proxyServerConnection.context.get(10, TimeUnit.SECONDS)).isNotNull(); |
| | | assertThat(onlineServerConnection.context.get(10, TimeUnit.SECONDS)).isNotNull(); |
| | |
| | | final MockServerConnectionFactory onlineServerConnectionFactory = |
| | | new MockServerConnectionFactory(onlineServerConnection); |
| | | final LDAPListener onlineServerListener = |
| | | newLDAPListener(new InetSocketAddress(0), onlineServerConnectionFactory); |
| | | new LDAPListener(new InetSocketAddress(0), onlineServerConnectionFactory); |
| | | |
| | | try { |
| | | // Connection pool and load balancing tests. |
| | | InetSocketAddress offlineAddress1 = findFreeSocketAddress(); |
| | | final ConnectionFactory offlineServer1 = newNamedConnectionFactory(newLDAPConnectionFactory( |
| | | offlineAddress1.getHostName(), offlineAddress1.getPort()), "offline1"); |
| | | final ConnectionFactory offlineServer1 = |
| | | Connections.newNamedConnectionFactory( |
| | | new LDAPConnectionFactory(offlineAddress1.getHostName(), |
| | | offlineAddress1.getPort()), "offline1"); |
| | | InetSocketAddress offlineAddress2 = findFreeSocketAddress(); |
| | | final ConnectionFactory offlineServer2 = newNamedConnectionFactory(newLDAPConnectionFactory( |
| | | offlineAddress2.getHostName(), offlineAddress2.getPort()), "offline2"); |
| | | final ConnectionFactory onlineServer = newNamedConnectionFactory(newLDAPConnectionFactory( |
| | | onlineServerListener.getHostName(), onlineServerListener.getPort()), "online"); |
| | | final ConnectionFactory offlineServer2 = |
| | | Connections.newNamedConnectionFactory( |
| | | new LDAPConnectionFactory(offlineAddress2.getHostName(), |
| | | offlineAddress2.getPort()), "offline2"); |
| | | final ConnectionFactory onlineServer = |
| | | Connections.newNamedConnectionFactory( |
| | | new LDAPConnectionFactory(onlineServerListener.getHostName(), |
| | | onlineServerListener.getPort()), "online"); |
| | | |
| | | // Round robin. |
| | | final ConnectionFactory loadBalancer = |
| | |
| | | new MockServerConnectionFactory(proxyServerConnection); |
| | | |
| | | final LDAPListener proxyListener = |
| | | newLDAPListener(new InetSocketAddress(0), proxyServerConnectionFactory); |
| | | new LDAPListener(new InetSocketAddress(0), proxyServerConnectionFactory); |
| | | try { |
| | | // Connect, bind, and close. |
| | | final Connection connection = |
| | | newLDAPConnectionFactory(proxyListener.getHostName(), proxyListener.getPort()).getConnection(); |
| | | new LDAPConnectionFactory(proxyListener.getHostName(), |
| | | proxyListener.getPort()).getConnection(); |
| | | try { |
| | | connection.bind("cn=test", "password".toCharArray()); |
| | | |
| | |
| | | final MockServerConnectionFactory onlineServerConnectionFactory = |
| | | new MockServerConnectionFactory(onlineServerConnection); |
| | | final LDAPListener onlineServerListener = |
| | | newLDAPListener(findFreeSocketAddress(), onlineServerConnectionFactory); |
| | | new LDAPListener(findFreeSocketAddress(), onlineServerConnectionFactory); |
| | | |
| | | try { |
| | | final MockServerConnection proxyServerConnection = new MockServerConnection(); |
| | |
| | | // First attempt offline server. |
| | | InetSocketAddress offlineAddress = findFreeSocketAddress(); |
| | | LDAPConnectionFactory lcf = |
| | | newLDAPConnectionFactory(offlineAddress.getHostName(), offlineAddress.getPort()); |
| | | new LDAPConnectionFactory(offlineAddress.getHostName(), |
| | | offlineAddress.getPort()); |
| | | try { |
| | | // This is expected to fail. |
| | | lcf.getConnection().close(); |
| | |
| | | } catch (final ConnectionException ce) { |
| | | // This is expected - so go to online server. |
| | | try { |
| | | lcf = newLDAPConnectionFactory( |
| | | lcf = |
| | | new LDAPConnectionFactory( |
| | | onlineServerListener.getHostName(), |
| | | onlineServerListener.getPort()); |
| | | lcf.getConnection().close(); |
| | |
| | | } |
| | | |
| | | }; |
| | | final LDAPListener proxyListener = newLDAPListener(findFreeSocketAddress(), proxyServerConnectionFactory); |
| | | final LDAPListener proxyListener = |
| | | new LDAPListener(findFreeSocketAddress(), proxyServerConnectionFactory); |
| | | try { |
| | | // Connect and close. |
| | | final Connection connection = |
| | | newLDAPConnectionFactory(proxyListener.getHostName(), proxyListener.getPort()).getConnection(); |
| | | new LDAPConnectionFactory(proxyListener.getHostName(), |
| | | proxyListener.getPort()).getConnection(); |
| | | |
| | | assertThat(proxyServerConnection.context.get(10, TimeUnit.SECONDS)).isNotNull(); |
| | | assertThat(onlineServerConnection.context.get(10, TimeUnit.SECONDS)).isNotNull(); |
| | |
| | | final MockServerConnectionFactory onlineServerConnectionFactory = |
| | | new MockServerConnectionFactory(onlineServerConnection); |
| | | final LDAPListener onlineServerListener = |
| | | newLDAPListener(findFreeSocketAddress(), onlineServerConnectionFactory); |
| | | new LDAPListener(findFreeSocketAddress(), onlineServerConnectionFactory); |
| | | |
| | | try { |
| | | final MockServerConnection proxyServerConnection = new MockServerConnection() { |
| | |
| | | throws UnsupportedOperationException { |
| | | // First attempt offline server. |
| | | InetSocketAddress offlineAddress = findFreeSocketAddress(); |
| | | LDAPConnectionFactory lcf = |
| | | newLDAPConnectionFactory(offlineAddress.getHostName(), offlineAddress.getPort()); |
| | | LDAPConnectionFactory lcf = new LDAPConnectionFactory(offlineAddress.getHostName(), |
| | | offlineAddress.getPort()); |
| | | try { |
| | | // This is expected to fail. |
| | | lcf.getConnection().close(); |
| | |
| | | } catch (final ConnectionException ce) { |
| | | // This is expected - so go to online server. |
| | | try { |
| | | lcf = newLDAPConnectionFactory(onlineServerListener.getHostName(), |
| | | lcf = new LDAPConnectionFactory(onlineServerListener.getHostName(), |
| | | onlineServerListener.getPort()); |
| | | lcf.getConnection().close(); |
| | | resultHandler.handleResult(Responses.newBindResult(ResultCode.SUCCESS)); |
| | |
| | | final MockServerConnectionFactory proxyServerConnectionFactory = |
| | | new MockServerConnectionFactory(proxyServerConnection); |
| | | final LDAPListener proxyListener = |
| | | newLDAPListener(findFreeSocketAddress(), proxyServerConnectionFactory); |
| | | new LDAPListener(findFreeSocketAddress(), proxyServerConnectionFactory); |
| | | try { |
| | | // Connect, bind, and close. |
| | | final Connection connection = |
| | | newLDAPConnectionFactory(proxyListener.getHostName(), proxyListener.getPort()).getConnection(); |
| | | new LDAPConnectionFactory(proxyListener.getHostName(), |
| | | proxyListener.getPort()).getConnection(); |
| | | try { |
| | | connection.bind("cn=test", "password".toCharArray()); |
| | | |
| | |
| | | final MockServerConnectionFactory factory = |
| | | new MockServerConnectionFactory(serverConnection); |
| | | final LDAPListenerOptions options = new LDAPListenerOptions().setMaxRequestSize(2048); |
| | | final LDAPListener listener = newLDAPListener(findFreeSocketAddress(), factory, options); |
| | | final LDAPListener listener = new LDAPListener(findFreeSocketAddress(), factory, options); |
| | | |
| | | Connection connection = null; |
| | | try { |
| | | connection = |
| | | newLDAPConnectionFactory(listener.getHostName(), listener.getPort()).getConnection(); |
| | | connection = new LDAPConnectionFactory(listener.getHostName(), listener.getPort()). |
| | | getConnection(); |
| | | |
| | | // Small request |
| | | connection.bind("cn=test", "password".toCharArray()); |
| | |
| | | final MockServerConnection serverConnection = new MockServerConnection(); |
| | | final MockServerConnectionFactory factory = |
| | | new MockServerConnectionFactory(serverConnection); |
| | | final LDAPListener listener = newLDAPListener(findFreeSocketAddress(), factory); |
| | | final LDAPListener listener = new LDAPListener(findFreeSocketAddress(), factory); |
| | | |
| | | final Connection connection; |
| | | try { |
| | | // Connect and bind. |
| | | connection = newLDAPConnectionFactory(listener.getHostName(), listener.getPort()).getConnection(); |
| | | connection = new LDAPConnectionFactory(listener.getHostName(), listener.getPort()).getConnection(); |
| | | try { |
| | | connection.bind("cn=test", "password".toCharArray()); |
| | | } catch (final LdapException e) { |
| | |
| | | try { |
| | | // Connect and bind. |
| | | final Connection failedConnection = |
| | | newLDAPConnectionFactory(listener.getHostName(), listener.getPort()).getConnection(); |
| | | new LDAPConnectionFactory(listener.getHostName(), |
| | | listener.getPort()).getConnection(); |
| | | failedConnection.close(); |
| | | connection.close(); |
| | | fail("Connection attempt to closed listener succeeded unexpectedly"); |
| | |
| | | import org.forgerock.opendj.ldap.DecodeException; |
| | | import org.forgerock.opendj.ldap.DecodeOptions; |
| | | import org.forgerock.opendj.ldap.Entry; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.Filter; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.ModificationType; |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.RootDSE; |
| | |
| | | import org.forgerock.opendj.ldif.ConnectionEntryReader; |
| | | import org.forgerock.opendj.ldif.LDIFEntryWriter; |
| | | |
| | | import static org.forgerock.opendj.ldap.Connections.*; |
| | | |
| | | /** |
| | | * This command-line client demonstrates use of LDAP controls. The client takes |
| | | * as arguments the host and port for the directory server, and expects to find |
| | |
| | | System.err.println("For example: localhost 1389"); |
| | | System.exit(1); |
| | | } |
| | | final String hostName = args[0]; |
| | | final String host = args[0]; |
| | | final int port = Integer.parseInt(args[1]); |
| | | |
| | | final LDAPConnectionFactory factory = newLDAPConnectionFactory(hostName, port); |
| | | final LDAPConnectionFactory factory = new LDAPConnectionFactory(host, port); |
| | | Connection connection = null; |
| | | try { |
| | | connection = factory.getConnection(); |
| | |
| | | import java.util.Collection; |
| | | |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.RootDSE; |
| | | import org.forgerock.opendj.ldap.requests.PasswordModifyExtendedRequest; |
| | | import org.forgerock.opendj.ldap.requests.Requests; |
| | |
| | | import org.forgerock.opendj.ldap.responses.Result; |
| | | import org.forgerock.opendj.ldap.responses.WhoAmIExtendedResult; |
| | | |
| | | import static org.forgerock.opendj.ldap.Connections.*; |
| | | |
| | | /** |
| | | * This command-line client demonstrates use of LDAP extended operations. The |
| | | * client takes as arguments the host and port for the directory server, and |
| | |
| | | System.err.println("For example: localhost 1389"); |
| | | System.exit(1); |
| | | } |
| | | final String hostName = args[0]; |
| | | final String host = args[0]; |
| | | final int port = Integer.parseInt(args[1]); |
| | | |
| | | final LDAPConnectionFactory factory = newLDAPConnectionFactory(hostName, port); |
| | | final LDAPConnectionFactory factory = new LDAPConnectionFactory(host, port); |
| | | Connection connection = null; |
| | | |
| | | try { |
| | |
| | | import java.io.IOException; |
| | | |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.SearchScope; |
| | | import org.forgerock.opendj.ldap.controls.GenericControl; |
| | |
| | | import org.forgerock.opendj.ldif.ConnectionEntryReader; |
| | | import org.forgerock.opendj.ldif.LDIFEntryWriter; |
| | | |
| | | import static org.forgerock.opendj.ldap.Connections.*; |
| | | |
| | | /** |
| | | * An example client application which searches Microsoft Active Directory |
| | | * synchronously, using {@link GenericControl} to pass the <a |
| | |
| | | final LDIFEntryWriter writer = new LDIFEntryWriter(System.out); |
| | | |
| | | // Connect and bind to the server. |
| | | final LDAPConnectionFactory factory = newLDAPConnectionFactory(hostName, port); |
| | | final LDAPConnectionFactory factory = |
| | | new LDAPConnectionFactory(hostName, port); |
| | | Connection connection = null; |
| | | |
| | | try { |
| | |
| | | import java.io.IOException; |
| | | |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.SearchScope; |
| | | import org.forgerock.opendj.ldap.responses.SearchResultEntry; |
| | | import org.forgerock.opendj.ldif.LDIFEntryWriter; |
| | | |
| | | import static org.forgerock.opendj.ldap.Connections.*; |
| | | |
| | | /** |
| | | * Demonstrates accessing server information about capabilities and schema. |
| | | */ |
| | | public final class GetInfo { |
| | | /** Connection information. */ |
| | | private static String hostName; |
| | | private static String host; |
| | | private static int port; |
| | | /** The kind of server information to request (all, controls, extops). */ |
| | | private static String infoType; |
| | |
| | | */ |
| | | private static void connect() { |
| | | // --- JCite --- |
| | | final LDAPConnectionFactory factory = newLDAPConnectionFactory(hostName, port); |
| | | final LDAPConnectionFactory factory = new LDAPConnectionFactory(host, port); |
| | | Connection connection = null; |
| | | |
| | | try { |
| | |
| | | attributeList); // Return these requested attributes. |
| | | |
| | | final LDIFEntryWriter writer = new LDIFEntryWriter(System.out); |
| | | writer.writeComment("Root DSE for LDAP server at " + hostName + ":" + port); |
| | | writer.writeComment("Root DSE for LDAP server at " + host + ":" + port); |
| | | if (entry != null) { |
| | | writer.writeEntry(entry); |
| | | } |
| | |
| | | giveUp(); |
| | | } |
| | | |
| | | hostName = args[0]; |
| | | host = args[0]; |
| | | port = Integer.parseInt(args[1]); |
| | | infoType = args[2]; |
| | | final String infoTypeLc = infoType.toLowerCase(); |
| | |
| | | import java.io.InputStream; |
| | | |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldif.ChangeRecord; |
| | | import org.forgerock.opendj.ldif.ConnectionChangeRecordWriter; |
| | | import org.forgerock.opendj.ldif.LDIFChangeRecordReader; |
| | | |
| | | import static org.forgerock.opendj.ldap.Connections.*; |
| | | |
| | | /** |
| | | * An example client application which applies update operations to a Directory |
| | | * Server. The update operations will be read from an LDIF file, or stdin if no |
| | |
| | | final LDIFChangeRecordReader reader = new LDIFChangeRecordReader(ldif); |
| | | |
| | | // Connect and bind to the server. |
| | | final LDAPConnectionFactory factory = newLDAPConnectionFactory(hostName, port); |
| | | final LDAPConnectionFactory factory = new LDAPConnectionFactory(hostName, port); |
| | | Connection connection = null; |
| | | |
| | | try { |
| | |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.DN; |
| | | import org.forgerock.opendj.ldap.Entry; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.LinkedHashMapEntry; |
| | | import org.forgerock.opendj.ldap.ModificationType; |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | |
| | | import org.forgerock.opendj.ldap.schema.Schema; |
| | | import org.forgerock.opendj.ldif.LDIFEntryWriter; |
| | | |
| | | import static org.forgerock.opendj.ldap.Connections.*; |
| | | |
| | | /** |
| | | * This command-line client demonstrates parsing entry attribute values to |
| | | * objects. The client takes as arguments the host and port for the directory |
| | |
| | | System.err.println("For example: localhost 1389"); |
| | | System.exit(1); |
| | | } |
| | | final String hostName = args[0]; |
| | | final String host = args[0]; |
| | | final int port = Integer.parseInt(args[1]); |
| | | |
| | | // --- JCite --- |
| | | final LDAPConnectionFactory factory = newLDAPConnectionFactory(hostName, port); |
| | | final LDAPConnectionFactory factory = new LDAPConnectionFactory(host, port); |
| | | Connection connection = null; |
| | | try { |
| | | connection = factory.getConnection(); |
| | |
| | | |
| | | package org.forgerock.opendj.examples; |
| | | |
| | | import java.nio.charset.Charset; |
| | | import java.security.GeneralSecurityException; |
| | | |
| | | import javax.net.ssl.SSLContext; |
| | | |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.DN; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.LDAPOptions; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.ModificationType; |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.SSLContextBuilder; |
| | |
| | | import org.forgerock.opendj.ldap.requests.ModifyRequest; |
| | | import org.forgerock.opendj.ldap.requests.Requests; |
| | | |
| | | import static org.forgerock.opendj.ldap.Connections.*; |
| | | import javax.net.ssl.SSLContext; |
| | | import java.nio.charset.Charset; |
| | | import java.security.GeneralSecurityException; |
| | | |
| | | /** |
| | | * This command-line client demonstrates how to reset a user password in |
| | |
| | | |
| | | Connection connection = null; |
| | | try { |
| | | final LDAPConnectionFactory factory = newLDAPConnectionFactory(host, port, getTrustAllOptions()); |
| | | final LDAPConnectionFactory factory = |
| | | new LDAPConnectionFactory(host, port, getTrustAllOptions()); |
| | | connection = factory.getConnection(); |
| | | connection.bind(bindDN, bindPassword.toCharArray()); |
| | | |
| | |
| | | import java.io.IOException; |
| | | import java.util.LinkedList; |
| | | import java.util.List; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | import org.forgerock.opendj.ldap.ConnectionFactory; |
| | | import org.forgerock.opendj.ldap.Connections; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.LDAPClientContext; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.LDAPListener; |
| | | import org.forgerock.opendj.ldap.LDAPListenerOptions; |
| | | import org.forgerock.opendj.ldap.LDAPOptions; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.RequestContext; |
| | | import org.forgerock.opendj.ldap.RequestHandlerFactory; |
| | | import org.forgerock.opendj.ldap.RoundRobinLoadBalancingAlgorithm; |
| | | import org.forgerock.opendj.ldap.ServerConnectionFactory; |
| | | import org.forgerock.opendj.ldap.requests.BindRequest; |
| | | import org.forgerock.opendj.ldap.requests.Requests; |
| | | |
| | | import static org.forgerock.opendj.ldap.Connections.*; |
| | | |
| | | /** |
| | | * An LDAP load balancing proxy which forwards requests to one or more remote |
| | | * Directory Servers. This is implementation is very simple and is only intended |
| | |
| | | * </pre> |
| | | */ |
| | | public final class Proxy { |
| | | /** The timeout after which a connection will be marked as failed. */ |
| | | private static final long TIMEOUT_SECONDS = 3; |
| | | |
| | | /** The interval between keepalive pings. */ |
| | | private static final long HEARTBEAT_INTERVAL_SECONDS = 10; |
| | | |
| | | /** |
| | | * Main method. |
| | | * |
| | |
| | | for (int i = 4; i < args.length; i += 2) { |
| | | final String remoteAddress = args[i]; |
| | | final int remotePort = Integer.parseInt(args[i + 1]); |
| | | final LDAPOptions commonOptions = new LDAPOptions().setTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS); |
| | | |
| | | final BindRequest bindRequest = Requests.newSimpleBindRequest(proxyDN, proxyPassword.toCharArray()); |
| | | final LDAPOptions options = new LDAPOptions(commonOptions).setBindRequest(bindRequest); |
| | | factories.add(newCachedConnectionPool(newLDAPConnectionFactory(remoteAddress, remotePort, options))); |
| | | |
| | | final LDAPOptions heartBeatOptions = |
| | | new LDAPOptions(commonOptions).setHeartBeatInterval(HEARTBEAT_INTERVAL_SECONDS, TimeUnit.SECONDS); |
| | | bindFactories.add(newCachedConnectionPool( |
| | | newLDAPConnectionFactory(remoteAddress, remotePort, heartBeatOptions))); |
| | | factories.add(Connections.newCachedConnectionPool(Connections |
| | | .newAuthenticatedConnectionFactory(Connections |
| | | .newHeartBeatConnectionFactory(new LDAPConnectionFactory(remoteAddress, |
| | | remotePort)), Requests.newSimpleBindRequest(proxyDN, |
| | | proxyPassword.toCharArray())))); |
| | | bindFactories.add(Connections.newCachedConnectionPool(Connections |
| | | .newHeartBeatConnectionFactory(new LDAPConnectionFactory(remoteAddress, |
| | | remotePort)))); |
| | | } |
| | | // --- JCite pools --- |
| | | |
| | |
| | | final LDAPListenerOptions options = new LDAPListenerOptions().setBacklog(4096); |
| | | LDAPListener listener = null; |
| | | try { |
| | | listener = newLDAPListener(localAddress, localPort, connectionHandler, options); |
| | | listener = new LDAPListener(localAddress, localPort, connectionHandler, options); |
| | | System.out.println("Press any key to stop the server..."); |
| | | System.in.read(); |
| | | } catch (final IOException e) { |
| | |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.DN; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.schema.AttributeType; |
| | | import org.forgerock.opendj.ldap.schema.MatchingRule; |
| | | import org.forgerock.opendj.ldap.schema.ObjectClass; |
| | | import org.forgerock.opendj.ldap.schema.Schema; |
| | | import org.forgerock.opendj.ldap.schema.Syntax; |
| | | |
| | | import static org.forgerock.opendj.ldap.Connections.*; |
| | | |
| | | /** |
| | | * An example client application which prints a summary of the schema on the |
| | | * named server as well as any warnings encountered while parsing the schema. |
| | |
| | | |
| | | // --- JCite --- |
| | | // Connect and bind to the server. |
| | | final LDAPConnectionFactory factory = newLDAPConnectionFactory(hostName, port); |
| | | final LDAPConnectionFactory factory = new LDAPConnectionFactory(hostName, port); |
| | | Connection connection = null; |
| | | |
| | | try { |
| | |
| | | import java.util.HashSet; |
| | | import java.util.List; |
| | | import java.util.Set; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | import org.forgerock.opendj.ldap.Attribute; |
| | | import org.forgerock.opendj.ldap.AttributeDescription; |
| | |
| | | import org.forgerock.opendj.ldap.ConnectionFactory; |
| | | import org.forgerock.opendj.ldap.Connections; |
| | | import org.forgerock.opendj.ldap.DN; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.Filter; |
| | | import org.forgerock.opendj.ldap.IntermediateResponseHandler; |
| | | import org.forgerock.opendj.ldap.LDAPClientContext; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.LDAPListener; |
| | | import org.forgerock.opendj.ldap.LDAPListenerOptions; |
| | | import org.forgerock.opendj.ldap.LDAPOptions; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.Modification; |
| | | import org.forgerock.opendj.ldap.RequestContext; |
| | | import org.forgerock.opendj.ldap.RequestHandler; |
| | |
| | | import org.forgerock.opendj.ldap.responses.SearchResultReference; |
| | | import org.forgerock.opendj.ldap.schema.AttributeType; |
| | | |
| | | import static org.forgerock.opendj.ldap.Connections.*; |
| | | |
| | | /** |
| | | * This example is based on the {@link Proxy}. This example does no load |
| | | * balancing, but instead rewrites attribute descriptions and DN suffixes in |
| | |
| | | * and {@code proxyUserPassword} to {@code password}. |
| | | */ |
| | | public final class RewriterProxy { |
| | | |
| | | /** The timeout after which a connection will be marked as failed. */ |
| | | private static final long TIMEOUT_SECONDS = 3; |
| | | |
| | | private static final class Rewriter implements RequestHandler<RequestContext> { |
| | | |
| | | /** This example hard codes the attribute... */ |
| | |
| | | final String proxyPassword = args[3]; |
| | | final String remoteAddress = args[4]; |
| | | final int remotePort = Integer.parseInt(args[5]); |
| | | final LDAPOptions factoryOptions = new LDAPOptions() |
| | | .setBindRequest(Requests.newSimpleBindRequest(proxyDN, proxyPassword.toCharArray())) |
| | | .setTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS); |
| | | |
| | | // Create connection factories. |
| | | final ConnectionFactory factory = newCachedConnectionPool( |
| | | newLDAPConnectionFactory(remoteAddress, remotePort, factoryOptions)); |
| | | final ConnectionFactory bindFactory = newCachedConnectionPool( |
| | | newLDAPConnectionFactory(remoteAddress, remotePort)); |
| | | final ConnectionFactory factory = |
| | | Connections.newCachedConnectionPool(Connections.newAuthenticatedConnectionFactory( |
| | | new LDAPConnectionFactory(remoteAddress, remotePort), Requests |
| | | .newSimpleBindRequest(proxyDN, proxyPassword.toCharArray()))); |
| | | final ConnectionFactory bindFactory = |
| | | Connections.newCachedConnectionPool(new LDAPConnectionFactory(remoteAddress, |
| | | remotePort)); |
| | | |
| | | /* |
| | | * Create a server connection adapter which will create a new proxy |
| | |
| | | final LDAPListenerOptions options = new LDAPListenerOptions().setBacklog(4096); |
| | | LDAPListener listener = null; |
| | | try { |
| | | listener = newLDAPListener(localAddress, localPort, connectionHandler, options); |
| | | listener = new LDAPListener(localAddress, localPort, connectionHandler, options); |
| | | System.out.println("Press any key to stop the server..."); |
| | | System.in.read(); |
| | | } catch (final IOException e) { |
| | |
| | | import javax.net.ssl.SSLContext; |
| | | |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.LDAPOptions; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.SSLContextBuilder; |
| | | import org.forgerock.opendj.ldap.TrustManagers; |
| | | import org.forgerock.opendj.ldap.requests.PlainSASLBindRequest; |
| | | import org.forgerock.opendj.ldap.requests.Requests; |
| | | |
| | | import static org.forgerock.opendj.ldap.Connections.*; |
| | | |
| | | /** |
| | | * An example client application which performs SASL PLAIN authentication to a |
| | | * directory server over LDAP with StartTLS. This example takes the following |
| | |
| | | |
| | | // --- JCite --- |
| | | try { |
| | | final LDAPConnectionFactory factory = newLDAPConnectionFactory(host, port, getTrustAllOptions()); |
| | | final LDAPConnectionFactory factory = |
| | | new LDAPConnectionFactory(host, port, getTrustAllOptions()); |
| | | connection = factory.getConnection(); |
| | | PlainSASLBindRequest request = |
| | | Requests.newPlainSASLBindRequest(authcid, passwd.toCharArray()) |
| | |
| | | import java.util.Arrays; |
| | | |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.SearchScope; |
| | | import org.forgerock.opendj.ldap.responses.SearchResultEntry; |
| | |
| | | import org.forgerock.opendj.ldif.ConnectionEntryReader; |
| | | import org.forgerock.opendj.ldif.LDIFEntryWriter; |
| | | |
| | | import static org.forgerock.opendj.ldap.Connections.*; |
| | | |
| | | /** |
| | | * An example client application which searches a Directory Server. This example |
| | | * takes the following command line parameters: |
| | |
| | | final LDIFEntryWriter writer = new LDIFEntryWriter(System.out); |
| | | |
| | | // Connect and bind to the server. |
| | | final LDAPConnectionFactory factory = newLDAPConnectionFactory(hostName, port); |
| | | final LDAPConnectionFactory factory = new LDAPConnectionFactory(hostName, port); |
| | | Connection connection = null; |
| | | |
| | | try { |
| | |
| | | import java.util.concurrent.CountDownLatch; |
| | | |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.Connections; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.LdapPromise; |
| | |
| | | |
| | | // --- Using Promises --- |
| | | // Initiate the asynchronous connect, bind, and search. |
| | | final LDAPConnectionFactory factory = Connections.newLDAPConnectionFactory(hostName, port); |
| | | final LDAPConnectionFactory factory = new LDAPConnectionFactory(hostName, port); |
| | | |
| | | factory.getConnectionAsync() |
| | | .thenAsync(new AsyncFunction<Connection, BindResult, LdapException>() { |
| | |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.DN; |
| | | import org.forgerock.opendj.ldap.Filter; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.SearchScope; |
| | | import org.forgerock.opendj.ldap.responses.SearchResultEntry; |
| | | |
| | | import static org.forgerock.opendj.ldap.Connections.*; |
| | | |
| | | /** |
| | | * An interactive command-line client that performs a search and subsequent |
| | | * simple bind. The client prompts for email address and for a password, and |
| | |
| | | System.err.println("For example: localhost 1389 dc=example,dc=com"); |
| | | System.exit(1); |
| | | } |
| | | String hostName = args[0]; |
| | | String host = args[0]; |
| | | int port = Integer.parseInt(args[1]); |
| | | String baseDN = args[2]; |
| | | |
| | |
| | | char[] password = c.readPassword("Password: "); |
| | | |
| | | // Search using mail address, and then bind with the DN and password. |
| | | final LDAPConnectionFactory factory = newLDAPConnectionFactory(hostName, port); |
| | | final LDAPConnectionFactory factory = new LDAPConnectionFactory(host, port); |
| | | Connection connection = null; |
| | | try { |
| | | connection = factory.getConnection(); |
| | |
| | | import org.forgerock.opendj.ldap.TrustManagers; |
| | | import org.forgerock.opendj.ldif.LDIFEntryReader; |
| | | |
| | | import static org.forgerock.opendj.ldap.Connections.*; |
| | | |
| | | /** |
| | | * An LDAP directory server which exposes data contained in an LDIF file. This |
| | | * is implementation is very simple and is only intended as an example: |
| | |
| | | } |
| | | }; |
| | | |
| | | listener = newLDAPListener(localAddress, localPort, sslWrapper, options); |
| | | listener = new LDAPListener(localAddress, localPort, sslWrapper, options); |
| | | } else { |
| | | // No SSL. |
| | | listener = newLDAPListener(localAddress, localPort, connectionHandler, options); |
| | | listener = new LDAPListener(localAddress, localPort, connectionHandler, options); |
| | | } |
| | | System.out.println("Press any key to stop the server..."); |
| | | System.in.read(); |
| | |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.Entries; |
| | | import org.forgerock.opendj.ldap.Entry; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.LinkedHashMapEntry; |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.TreeMapEntry; |
| | | import org.forgerock.opendj.ldap.requests.ModifyRequest; |
| | | import org.forgerock.opendj.ldif.LDIFEntryWriter; |
| | | |
| | | import static org.forgerock.opendj.ldap.Connections.*; |
| | | |
| | | /** |
| | | * A command-line client that creates, updates, renames, and deletes a |
| | | * short-lived entry in order to demonstrate LDAP write operations using the |
| | |
| | | System.err.println("For example: localhost 1389"); |
| | | System.exit(1); |
| | | } |
| | | String hostName = args[0]; |
| | | String host = args[0]; |
| | | int port = Integer.parseInt(args[1]); |
| | | |
| | | // User credentials of a "Directory Administrators" group member. |
| | |
| | | |
| | | LDIFEntryWriter writer = new LDIFEntryWriter(System.out); |
| | | |
| | | final LDAPConnectionFactory factory = newLDAPConnectionFactory(hostName, port); |
| | | final LDAPConnectionFactory factory = new LDAPConnectionFactory(host, port); |
| | | Connection connection = null; |
| | | try { |
| | | connection = factory.getConnection(); |
| | |
| | | import javax.net.ssl.TrustManager; |
| | | |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.LDAPOptions; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.SSLContextBuilder; |
| | | import org.forgerock.opendj.ldap.TrustManagers; |
| | | |
| | | import static org.forgerock.opendj.ldap.Connections.*; |
| | | |
| | | /** |
| | | * An example client application which performs simple authentication to a |
| | | * directory server. This example takes the following command line parameters: |
| | |
| | | * Authenticate over LDAP. |
| | | */ |
| | | private static void connect() { |
| | | final LDAPConnectionFactory factory = newLDAPConnectionFactory(hostName, port); |
| | | final LDAPConnectionFactory factory = new LDAPConnectionFactory(host, port); |
| | | Connection connection = null; |
| | | |
| | | try { |
| | |
| | | try { |
| | | |
| | | final LDAPConnectionFactory factory = |
| | | newLDAPConnectionFactory(hostName, port, getTrustOptions(hostName, keystore, storepass)); |
| | | new LDAPConnectionFactory(host, port, |
| | | getTrustOptions(host, keystore, storepass)); |
| | | connection = factory.getConnection(); |
| | | connection.bind(bindDN, bindPassword.toCharArray()); |
| | | |
| | |
| | | Connection connection = null; |
| | | |
| | | try { |
| | | final LDAPConnectionFactory factory = newLDAPConnectionFactory(hostName, port, getTrustAllOptions()); |
| | | final LDAPConnectionFactory factory = |
| | | new LDAPConnectionFactory(host, port, getTrustAllOptions()); |
| | | connection = factory.getConnection(); |
| | | connection.bind(bindDN, bindPassword.toCharArray()); |
| | | System.out.println("Authenticated as " + bindDN + "."); |
| | |
| | | // trustAllConnect(); |
| | | } |
| | | |
| | | private static String hostName; |
| | | private static String host; |
| | | private static int port; |
| | | private static String bindDN; |
| | | private static String bindPassword; |
| | |
| | | giveUp(); |
| | | } |
| | | |
| | | hostName = args[0]; |
| | | host = args[0]; |
| | | port = Integer.parseInt(args[1]); |
| | | bindDN = args[2]; |
| | | bindPassword = args[3]; |
| | |
| | | import java.util.Collection; |
| | | |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.ModificationType; |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.RootDSE; |
| | |
| | | import org.forgerock.opendj.ldap.requests.Requests; |
| | | import org.forgerock.opendj.ldap.responses.CompareResult; |
| | | |
| | | import static org.forgerock.opendj.ldap.Connections.*; |
| | | |
| | | /** |
| | | * This command-line client demonstrates adding and removing a member from a |
| | | * (potentially large) static group. |
| | |
| | | if (args.length != 5) { |
| | | printUsage(); |
| | | } |
| | | final String hostName = args[0]; |
| | | final String host = args[0]; |
| | | final int port = Integer.parseInt(args[1]); |
| | | final String groupDN = args[2]; |
| | | final String memberDN = args[3]; |
| | | final ModificationType modType = getModificationType(args[4]); |
| | | |
| | | final LDAPConnectionFactory factory = newLDAPConnectionFactory(hostName, port); |
| | | final LDAPConnectionFactory factory = new LDAPConnectionFactory(host, port); |
| | | Connection connection = null; |
| | | try { |
| | | connection = factory.getConnection(); |
| | |
| | | |
| | | package org.forgerock.opendj.examples; |
| | | |
| | | import java.io.IOException; |
| | | |
| | | import org.forgerock.opendj.io.ASN1; |
| | | import org.forgerock.opendj.io.ASN1Writer; |
| | | import org.forgerock.opendj.ldap.ByteStringBuilder; |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.DecodeOptions; |
| | | import org.forgerock.opendj.ldap.Entry; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.ModificationType; |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.SearchScope; |
| | |
| | | import org.forgerock.opendj.ldap.responses.SearchResultEntry; |
| | | import org.forgerock.opendj.ldif.LDIFEntryWriter; |
| | | |
| | | import static org.forgerock.opendj.ldap.Connections.*; |
| | | import java.io.IOException; |
| | | |
| | | /** |
| | | * An example client application which uses |
| | |
| | | final LDIFEntryWriter writer = new LDIFEntryWriter(System.out); |
| | | |
| | | // Connect and bind to the server. |
| | | final LDAPConnectionFactory factory = newLDAPConnectionFactory(hostName, port); |
| | | final LDAPConnectionFactory factory = |
| | | new LDAPConnectionFactory(hostName, port); |
| | | Connection connection = null; |
| | | |
| | | // Prepare the value for the GenericControl. |
| | |
| | | import static com.forgerock.opendj.cli.ArgumentConstants.*; |
| | | import static com.forgerock.opendj.ldap.tools.ToolsMessages.*; |
| | | import static com.forgerock.opendj.cli.Utils.filterExitCode; |
| | | import static com.forgerock.opendj.ldap.tools.Utils.*; |
| | | |
| | | import static com.forgerock.opendj.ldap.tools.Utils.printErrorMessage; |
| | | import static org.forgerock.util.Utils.closeSilently; |
| | | |
| | | import java.io.FileInputStream; |
| | |
| | | import org.forgerock.opendj.ldap.controls.PreReadResponseControl; |
| | | import org.forgerock.opendj.ldap.controls.ProxiedAuthV2RequestControl; |
| | | import org.forgerock.opendj.ldap.requests.AddRequest; |
| | | import org.forgerock.opendj.ldap.requests.BindRequest; |
| | | import org.forgerock.opendj.ldap.requests.DeleteRequest; |
| | | import org.forgerock.opendj.ldap.requests.ModifyDNRequest; |
| | | import org.forgerock.opendj.ldap.requests.ModifyRequest; |
| | |
| | | int run(final String[] args) { |
| | | // Create the command-line argument parser for use with this program. |
| | | final LocalizableMessage toolDescription = INFO_LDAPMODIFY_TOOL_DESCRIPTION.get(); |
| | | final ArgumentParser argParser = new ArgumentParser(LDAPModify.class.getName(), toolDescription, false); |
| | | final ArgumentParser argParser = |
| | | new ArgumentParser(LDAPModify.class.getName(), toolDescription, false); |
| | | ConnectionFactoryProvider connectionFactoryProvider; |
| | | ConnectionFactory connectionFactory; |
| | | BindRequest bindRequest; |
| | | |
| | | BooleanArgument continueOnError; |
| | | // TODO: Remove this due to new LDIF reader api? |
| | |
| | | return 0; |
| | | } |
| | | |
| | | connectionFactory = connectionFactoryProvider.getConnectionFactory(); |
| | | bindRequest = connectionFactoryProvider.getBindRequest(); |
| | | connectionFactory = connectionFactoryProvider.getAuthenticatedConnectionFactory(); |
| | | } catch (final ArgumentException ae) { |
| | | final LocalizableMessage message = ERR_ERROR_PARSING_ARGS.get(ae.getMessage()); |
| | | errPrintln(message); |
| | |
| | | if (!noop.isPresent()) { |
| | | try { |
| | | connection = connectionFactory.getConnection(); |
| | | if (bindRequest != null) { |
| | | printPasswordPolicyResults(this, connection.bind(bindRequest)); |
| | | } |
| | | } catch (final LdapException ere) { |
| | | return printErrorMessage(this, ere); |
| | | return Utils.printErrorMessage(this, ere); |
| | | } |
| | | } |
| | | |
| | | Utils.printPasswordPolicyResults(this, connection); |
| | | |
| | | writer = new LDIFEntryWriter(getOutputStream()); |
| | | final VisitorImpl visitor = new VisitorImpl(); |
| | | ChangeRecordReader reader = null; |
| | |
| | | import org.forgerock.opendj.ldap.controls.SimplePagedResultsControl; |
| | | import org.forgerock.opendj.ldap.controls.VirtualListViewRequestControl; |
| | | import org.forgerock.opendj.ldap.controls.VirtualListViewResponseControl; |
| | | import org.forgerock.opendj.ldap.requests.BindRequest; |
| | | import org.forgerock.opendj.ldap.requests.Requests; |
| | | import org.forgerock.opendj.ldap.requests.SearchRequest; |
| | | import org.forgerock.opendj.ldap.responses.Result; |
| | |
| | | import static com.forgerock.opendj.cli.ArgumentConstants.*; |
| | | import static com.forgerock.opendj.cli.Utils.*; |
| | | import static com.forgerock.opendj.ldap.tools.ToolsMessages.*; |
| | | import static com.forgerock.opendj.ldap.tools.Utils.*; |
| | | |
| | | /** |
| | | * A tool that can be used to issue Search requests to the Directory Server. |
| | |
| | | "[filter] [attributes ...]"); |
| | | ConnectionFactoryProvider connectionFactoryProvider; |
| | | ConnectionFactory connectionFactory; |
| | | BindRequest bindRequest; |
| | | |
| | | BooleanArgument countEntries; |
| | | BooleanArgument dontWrap; |
| | |
| | | return 0; |
| | | } |
| | | |
| | | connectionFactory = connectionFactoryProvider.getConnectionFactory(); |
| | | bindRequest = connectionFactoryProvider.getBindRequest(); |
| | | connectionFactory = connectionFactoryProvider.getAuthenticatedConnectionFactory(); |
| | | } catch (final ArgumentException ae) { |
| | | final LocalizableMessage message = ERR_ERROR_PARSING_ARGS.get(ae.getMessage()); |
| | | errPrintln(message); |
| | |
| | | Connection connection; |
| | | try { |
| | | connection = connectionFactory.getConnection(); |
| | | if (bindRequest != null) { |
| | | printPasswordPolicyResults(this, connection.bind(bindRequest)); |
| | | } |
| | | } catch (final LdapException ere) { |
| | | return printErrorMessage(this, ere); |
| | | return Utils.printErrorMessage(this, ere); |
| | | } |
| | | |
| | | Utils.printPasswordPolicyResults(this, connection); |
| | | |
| | | try { |
| | | int filterIndex = 0; |
| | |
| | | |
| | | import com.forgerock.opendj.cli.ArgumentException; |
| | | import com.forgerock.opendj.cli.ArgumentParser; |
| | | import com.forgerock.opendj.cli.AuthenticatedConnectionFactory.AuthenticatedConnection; |
| | | import com.forgerock.opendj.cli.BooleanArgument; |
| | | import com.forgerock.opendj.cli.ConsoleApplication; |
| | | import com.forgerock.opendj.cli.IntegerArgument; |
| | |
| | | } |
| | | } else { |
| | | connection = this.connection; |
| | | if (!noRebind && app.getBindRequest() != null) { |
| | | if (!noRebind && connection instanceof AuthenticatedConnection) { |
| | | final AuthenticatedConnection ac = (AuthenticatedConnection) connection; |
| | | try { |
| | | connection.bindAsync(app.getBindRequest()).getOrThrow(); |
| | | ac.rebindAsync().getOrThrow(); |
| | | } catch (final InterruptedException e) { |
| | | // Ignore and check stop requested |
| | | continue; |
| | |
| | | */ |
| | | package com.forgerock.opendj.ldap.tools; |
| | | |
| | | import static com.forgerock.opendj.ldap.tools.ToolsMessages.*; |
| | | import static com.forgerock.opendj.cli.Utils.readBytesFromFile; |
| | | import static com.forgerock.opendj.cli.Utils.secondsToTimeString; |
| | | |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.DecodeException; |
| | | import org.forgerock.opendj.ldap.DecodeOptions; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | |
| | | |
| | | import com.forgerock.opendj.cli.ConsoleApplication; |
| | | import com.forgerock.opendj.ldap.controls.AccountUsabilityRequestControl; |
| | | import com.forgerock.opendj.cli.AuthenticatedConnectionFactory.AuthenticatedConnection; |
| | | import com.forgerock.opendj.util.StaticUtils; |
| | | |
| | | import static com.forgerock.opendj.cli.Utils.*; |
| | | import static com.forgerock.opendj.ldap.tools.ToolsMessages.*; |
| | | |
| | | /** |
| | | * This class provides utility functions for all the client side tools. |
| | | */ |
| | |
| | | return ere.getResult().getResultCode().intValue(); |
| | | } |
| | | |
| | | static void printPasswordPolicyResults(final ConsoleApplication app, BindResult result) { |
| | | try { |
| | | final AuthorizationIdentityResponseControl control = |
| | | result.getControl(AuthorizationIdentityResponseControl.DECODER, new DecodeOptions()); |
| | | if (control != null) { |
| | | final LocalizableMessage message = INFO_BIND_AUTHZID_RETURNED.get(control.getAuthorizationID()); |
| | | app.println(message); |
| | | } |
| | | } catch (final DecodeException e) { |
| | | app.errPrintln(ERR_DECODE_CONTROL_FAILURE.get(e.getLocalizedMessage())); |
| | | } |
| | | static void printPasswordPolicyResults(final ConsoleApplication app, final Connection connection) { |
| | | if (connection instanceof AuthenticatedConnection) { |
| | | final AuthenticatedConnection conn = (AuthenticatedConnection) connection; |
| | | final BindResult result = conn.getAuthenticatedBindResult(); |
| | | |
| | | try { |
| | | final PasswordExpiredResponseControl control = |
| | | result.getControl(PasswordExpiredResponseControl.DECODER, |
| | | new DecodeOptions()); |
| | | if (control != null) { |
| | | final LocalizableMessage message = INFO_BIND_PASSWORD_EXPIRED.get(); |
| | | app.println(message); |
| | | try { |
| | | final AuthorizationIdentityResponseControl control = |
| | | result.getControl(AuthorizationIdentityResponseControl.DECODER, |
| | | new DecodeOptions()); |
| | | if (control != null) { |
| | | final LocalizableMessage message = |
| | | INFO_BIND_AUTHZID_RETURNED.get(control.getAuthorizationID()); |
| | | app.println(message); |
| | | } |
| | | } catch (final DecodeException e) { |
| | | app.errPrintln(ERR_DECODE_CONTROL_FAILURE.get(e.getLocalizedMessage())); |
| | | } |
| | | } catch (final DecodeException e) { |
| | | app.errPrintln(ERR_DECODE_CONTROL_FAILURE.get(e.getLocalizedMessage())); |
| | | } |
| | | |
| | | try { |
| | | final PasswordExpiringResponseControl control = |
| | | result.getControl(PasswordExpiringResponseControl.DECODER, |
| | | new DecodeOptions()); |
| | | if (control != null) { |
| | | final LocalizableMessage timeString = |
| | | secondsToTimeString(control.getSecondsUntilExpiration()); |
| | | final LocalizableMessage message = INFO_BIND_PASSWORD_EXPIRING.get(timeString); |
| | | app.println(message); |
| | | } |
| | | } catch (final DecodeException e) { |
| | | app.errPrintln(ERR_DECODE_CONTROL_FAILURE.get(e.getLocalizedMessage())); |
| | | } |
| | | |
| | | try { |
| | | final PasswordPolicyResponseControl control = |
| | | result.getControl(PasswordPolicyResponseControl.DECODER, |
| | | new DecodeOptions()); |
| | | if (control != null) { |
| | | final PasswordPolicyErrorType errorType = control.getErrorType(); |
| | | if (errorType == PasswordPolicyErrorType.PASSWORD_EXPIRED) { |
| | | try { |
| | | final PasswordExpiredResponseControl control = |
| | | result.getControl(PasswordExpiredResponseControl.DECODER, |
| | | new DecodeOptions()); |
| | | if (control != null) { |
| | | final LocalizableMessage message = INFO_BIND_PASSWORD_EXPIRED.get(); |
| | | app.println(message); |
| | | } else if (errorType == PasswordPolicyErrorType.ACCOUNT_LOCKED) { |
| | | final LocalizableMessage message = INFO_BIND_ACCOUNT_LOCKED.get(); |
| | | app.println(message); |
| | | } else if (errorType == PasswordPolicyErrorType.CHANGE_AFTER_RESET) { |
| | | |
| | | final LocalizableMessage message = INFO_BIND_MUST_CHANGE_PASSWORD.get(); |
| | | app.println(message); |
| | | } |
| | | |
| | | final PasswordPolicyWarningType warningType = control.getWarningType(); |
| | | if (warningType == PasswordPolicyWarningType.TIME_BEFORE_EXPIRATION) { |
| | | final LocalizableMessage timeString = |
| | | secondsToTimeString(control.getWarningValue()); |
| | | final LocalizableMessage message = |
| | | INFO_BIND_PASSWORD_EXPIRING.get(timeString); |
| | | app.println(message); |
| | | } else if (warningType == PasswordPolicyWarningType.GRACE_LOGINS_REMAINING) { |
| | | final LocalizableMessage message = |
| | | INFO_BIND_GRACE_LOGINS_REMAINING.get(control.getWarningValue()); |
| | | app.println(message); |
| | | } |
| | | } catch (final DecodeException e) { |
| | | app.errPrintln(ERR_DECODE_CONTROL_FAILURE.get(e.getLocalizedMessage())); |
| | | } |
| | | } catch (final DecodeException e) { |
| | | app.errPrintln(ERR_DECODE_CONTROL_FAILURE.get(e.getLocalizedMessage())); |
| | | |
| | | try { |
| | | final PasswordExpiringResponseControl control = |
| | | result.getControl(PasswordExpiringResponseControl.DECODER, |
| | | new DecodeOptions()); |
| | | if (control != null) { |
| | | final LocalizableMessage timeString = |
| | | secondsToTimeString(control.getSecondsUntilExpiration()); |
| | | final LocalizableMessage message = INFO_BIND_PASSWORD_EXPIRING.get(timeString); |
| | | app.println(message); |
| | | } |
| | | } catch (final DecodeException e) { |
| | | app.errPrintln(ERR_DECODE_CONTROL_FAILURE.get(e.getLocalizedMessage())); |
| | | } |
| | | |
| | | try { |
| | | final PasswordPolicyResponseControl control = |
| | | result.getControl(PasswordPolicyResponseControl.DECODER, |
| | | new DecodeOptions()); |
| | | if (control != null) { |
| | | final PasswordPolicyErrorType errorType = control.getErrorType(); |
| | | if (errorType == PasswordPolicyErrorType.PASSWORD_EXPIRED) { |
| | | final LocalizableMessage message = INFO_BIND_PASSWORD_EXPIRED.get(); |
| | | app.println(message); |
| | | } else if (errorType == PasswordPolicyErrorType.ACCOUNT_LOCKED) { |
| | | final LocalizableMessage message = INFO_BIND_ACCOUNT_LOCKED.get(); |
| | | app.println(message); |
| | | } else if (errorType == PasswordPolicyErrorType.CHANGE_AFTER_RESET) { |
| | | |
| | | final LocalizableMessage message = INFO_BIND_MUST_CHANGE_PASSWORD.get(); |
| | | app.println(message); |
| | | } |
| | | |
| | | final PasswordPolicyWarningType warningType = control.getWarningType(); |
| | | if (warningType == PasswordPolicyWarningType.TIME_BEFORE_EXPIRATION) { |
| | | final LocalizableMessage timeString = |
| | | secondsToTimeString(control.getWarningValue()); |
| | | final LocalizableMessage message = |
| | | INFO_BIND_PASSWORD_EXPIRING.get(timeString); |
| | | app.println(message); |
| | | } else if (warningType == PasswordPolicyWarningType.GRACE_LOGINS_REMAINING) { |
| | | final LocalizableMessage message = |
| | | INFO_BIND_GRACE_LOGINS_REMAINING.get(control.getWarningValue()); |
| | | app.println(message); |
| | | } |
| | | } |
| | | } catch (final DecodeException e) { |
| | | app.errPrintln(ERR_DECODE_CONTROL_FAILURE.get(e.getLocalizedMessage())); |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | package org.forgerock.opendj.rest2ldap; |
| | | |
| | | import static org.forgerock.opendj.ldap.requests.Requests.newSearchRequest; |
| | | import static org.forgerock.opendj.ldap.schema.CoreSchema.getEntryUUIDAttributeType; |
| | | import static org.forgerock.opendj.rest2ldap.ReadOnUpdatePolicy.CONTROLS; |
| | | import static org.forgerock.opendj.rest2ldap.Utils.ensureNotNull; |
| | | |
| | | import java.io.IOException; |
| | | import java.security.GeneralSecurityException; |
| | | import java.util.ArrayList; |
| | |
| | | import org.forgerock.opendj.ldap.DN; |
| | | import org.forgerock.opendj.ldap.Entry; |
| | | import org.forgerock.opendj.ldap.EntryNotFoundException; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.FailoverLoadBalancingAlgorithm; |
| | | import org.forgerock.opendj.ldap.Filter; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.LDAPOptions; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.LinkedAttribute; |
| | | import org.forgerock.opendj.ldap.MultipleEntriesFoundException; |
| | | import org.forgerock.opendj.ldap.RDN; |
| | |
| | | import org.forgerock.opendj.ldap.SearchScope; |
| | | import org.forgerock.opendj.ldap.TimeoutResultException; |
| | | import org.forgerock.opendj.ldap.TrustManagers; |
| | | import org.forgerock.opendj.ldap.requests.BindRequest; |
| | | import org.forgerock.opendj.ldap.requests.Requests; |
| | | import org.forgerock.opendj.ldap.requests.SearchRequest; |
| | | import org.forgerock.opendj.ldap.schema.AttributeType; |
| | | import org.forgerock.opendj.ldap.schema.Schema; |
| | | |
| | | import static java.util.concurrent.TimeUnit.*; |
| | | |
| | | import static org.forgerock.opendj.ldap.Connections.*; |
| | | import static org.forgerock.opendj.ldap.requests.Requests.*; |
| | | import static org.forgerock.opendj.ldap.schema.CoreSchema.*; |
| | | import static org.forgerock.opendj.rest2ldap.ReadOnUpdatePolicy.*; |
| | | import static org.forgerock.opendj.rest2ldap.Utils.*; |
| | | |
| | | /** |
| | | * Provides core factory methods and builders for constructing LDAP resource |
| | | * collections. |
| | |
| | | Math.max(configuration.get("connectionPoolSize").defaultTo(10).asInteger(), 1); |
| | | final int heartBeatIntervalSeconds = |
| | | Math.max(configuration.get("heartBeatIntervalSeconds").defaultTo(30).asInteger(), 1); |
| | | final int heartBeatTimeoutMS = |
| | | Math.max(configuration.get("heartBeatTimeoutMilliSeconds").defaultTo(500).asInteger(), 100); |
| | | final LDAPOptions options = |
| | | new LDAPOptions().setHeartBeatInterval(heartBeatIntervalSeconds, SECONDS) |
| | | .setTimeout(heartBeatTimeoutMS, MILLISECONDS); |
| | | final int heartBeatTimeoutMilliSeconds = |
| | | Math.max(configuration.get("heartBeatTimeoutMilliSeconds").defaultTo(500) |
| | | .asInteger(), 100); |
| | | |
| | | // Parse authentication parameters. |
| | | final BindRequest bindRequest; |
| | | if (configuration.isDefined("authentication")) { |
| | | final JsonValue authn = configuration.get("authentication"); |
| | | if (authn.isDefined("simple")) { |
| | | final JsonValue simple = authn.get("simple"); |
| | | options.setBindRequest(newSimpleBindRequest(simple.get("bindDN").required().asString(), |
| | | simple.get("bindPassword").required().asString().toCharArray())); |
| | | bindRequest = |
| | | Requests.newSimpleBindRequest(simple.get("bindDN").required().asString(), |
| | | simple.get("bindPassword").required().asString().toCharArray()); |
| | | } else { |
| | | throw new IllegalArgumentException("Only simple authentication is supported"); |
| | | } |
| | | } else { |
| | | bindRequest = null; |
| | | } |
| | | |
| | | // Parse SSL/StartTLS parameters. |
| | | final ConnectionSecurity connectionSecurity = |
| | | configuration.get("connectionSecurity").defaultTo(ConnectionSecurity.NONE).asEnum( |
| | | ConnectionSecurity.class); |
| | | final LDAPOptions options = new LDAPOptions(); |
| | | if (connectionSecurity != ConnectionSecurity.NONE) { |
| | | try { |
| | | // Configure SSL. |
| | |
| | | throw new IllegalArgumentException("No primaryLDAPServers"); |
| | | } |
| | | final ConnectionFactory primary = |
| | | parseLDAPServers(primaryLDAPServers, connectionPoolSize, heartBeatIntervalSeconds, options); |
| | | parseLDAPServers(primaryLDAPServers, bindRequest, connectionPoolSize, |
| | | heartBeatIntervalSeconds, heartBeatTimeoutMilliSeconds, options); |
| | | |
| | | // Parse secondary data center(s). |
| | | final JsonValue secondaryLDAPServers = configuration.get("secondaryLDAPServers"); |
| | | ConnectionFactory secondary = null; |
| | | final ConnectionFactory secondary; |
| | | if (secondaryLDAPServers.isList()) { |
| | | if (secondaryLDAPServers.size() > 0) { |
| | | secondary = |
| | | parseLDAPServers(secondaryLDAPServers, connectionPoolSize, heartBeatIntervalSeconds, options); |
| | | parseLDAPServers(secondaryLDAPServers, bindRequest, connectionPoolSize, |
| | | heartBeatIntervalSeconds, heartBeatTimeoutMilliSeconds, options); |
| | | } else { |
| | | secondary = null; |
| | | } |
| | | } else if (!secondaryLDAPServers.isNull()) { |
| | | throw new IllegalArgumentException("Invalid secondaryLDAPServers configuration"); |
| | | } else { |
| | | secondary = null; |
| | | } |
| | | |
| | | // Create fail-over. |
| | |
| | | } |
| | | } |
| | | |
| | | private static ConnectionFactory parseLDAPServers(final JsonValue config, final int connectionPoolSize, |
| | | final int heartBeatIntervalSeconds, final LDAPOptions options) { |
| | | private static ConnectionFactory parseLDAPServers(final JsonValue config, |
| | | final BindRequest bindRequest, final int connectionPoolSize, |
| | | final int heartBeatIntervalSeconds, final int heartBeatTimeoutMilliSeconds, |
| | | final LDAPOptions options) { |
| | | final List<ConnectionFactory> servers = new ArrayList<ConnectionFactory>(config.size()); |
| | | |
| | | for (final JsonValue server : config) { |
| | | final String host = server.get("hostname").required().asString(); |
| | | final int port = server.get("port").required().asInteger(); |
| | | ConnectionFactory factory = newLDAPConnectionFactory(host, port, options); |
| | | ConnectionFactory factory = new LDAPConnectionFactory(host, port, options); |
| | | factory = |
| | | Connections.newHeartBeatConnectionFactory(factory, |
| | | heartBeatIntervalSeconds * 1000, heartBeatTimeoutMilliSeconds, |
| | | TimeUnit.MILLISECONDS); |
| | | if (bindRequest != null) { |
| | | factory = Connections.newAuthenticatedConnectionFactory(factory, bindRequest); |
| | | } |
| | | if (connectionPoolSize > 1) { |
| | | factory = newCachedConnectionPool(factory, 0, connectionPoolSize, 60L, SECONDS); |
| | | factory = |
| | | Connections.newCachedConnectionPool(factory, 0, connectionPoolSize, 60L, |
| | | TimeUnit.SECONDS); |
| | | } |
| | | servers.add(factory); |
| | | } |
| | | |
| | | if (servers.size() > 1) { |
| | | return newLoadBalancer(new RoundRobinLoadBalancingAlgorithm(servers, heartBeatIntervalSeconds, SECONDS)); |
| | | return Connections.newLoadBalancer(new RoundRobinLoadBalancingAlgorithm(servers, |
| | | heartBeatIntervalSeconds, TimeUnit.SECONDS)); |
| | | } else { |
| | | return servers.get(0); |
| | | } |
| | |
| | | import org.forgerock.opendj.ldap.AuthorizationException; |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.ConnectionFactory; |
| | | import org.forgerock.opendj.ldap.Connections; |
| | | import org.forgerock.opendj.ldap.ConstraintViolationException; |
| | | import org.forgerock.opendj.ldap.DN; |
| | | import org.forgerock.opendj.ldap.DecodeException; |
| | |
| | | import org.forgerock.opendj.ldap.Entry; |
| | | import org.forgerock.opendj.ldap.EntryNotFoundException; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.LDAPOptions; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.LinkedHashMapEntry; |
| | | import org.forgerock.opendj.ldap.ModificationType; |
| | |
| | | import org.testng.annotations.DataProvider; |
| | | import org.testng.annotations.Test; |
| | | |
| | | import static java.util.concurrent.TimeUnit.*; |
| | | |
| | | import static org.fest.assertions.Assertions.*; |
| | | import static org.fest.assertions.Fail.*; |
| | | import static org.forgerock.opendj.adapter.server2x.Adapters.*; |
| | | import static org.forgerock.opendj.adapter.server2x.EmbeddedServerTestCaseUtils.*; |
| | | import static org.forgerock.opendj.ldap.Connections.*; |
| | | import static org.forgerock.opendj.ldap.requests.Requests.*; |
| | | |
| | | /** |
| | | * This class defines a set of tests for the Adapters.class. |
| | |
| | | @Test |
| | | public class AdaptersTestCase extends ForgeRockTestCase { |
| | | |
| | | /** The timeout after which a connection will be marked as failed. */ |
| | | private static final long TIMEOUT_SEC = 3; |
| | | |
| | | private Integer getListenPort() { |
| | | return Integer.valueOf(CONFIG_PROPERTIES.getProperty("listen-port")); |
| | | } |
| | | |
| | | /** |
| | | * Provides an anonymous connection factories. |
| | | * |
| | |
| | | @DataProvider |
| | | public Object[][] anonymousConnectionFactories() { |
| | | return new Object[][] { |
| | | { newLDAPConnectionFactory("localhost", getListenPort()) }, |
| | | { newAnonymousConnectionFactory() } }; |
| | | { new LDAPConnectionFactory("localhost", |
| | | Integer.valueOf(CONFIG_PROPERTIES.getProperty("listen-port"))) }, |
| | | { Adapters.newAnonymousConnectionFactory() } }; |
| | | } |
| | | |
| | | /** |
| | |
| | | @DataProvider |
| | | public Object[][] rootConnectionFactories() { |
| | | return new Object[][] { |
| | | { newLDAPConnectionFactory( |
| | | "localhost", |
| | | getListenPort(), |
| | | new LDAPOptions() |
| | | .setBindRequest(newSimpleBindRequest("cn=directory manager", "password".toCharArray())) |
| | | .setTimeout(TIMEOUT_SEC, SECONDS)) }, |
| | | { newConnectionFactoryForUser(DN.valueOf("cn=directory manager")) } }; |
| | | { Connections.newAuthenticatedConnectionFactory( |
| | | new LDAPConnectionFactory("localhost", |
| | | Integer.valueOf(CONFIG_PROPERTIES.getProperty("listen-port"))), |
| | | Requests.newSimpleBindRequest("cn=directory manager", "password".toCharArray())) }, |
| | | { Adapters.newConnectionFactoryForUser(DN.valueOf("cn=directory manager")) } }; |
| | | } |
| | | |
| | | /** |
| | |
| | | @Test |
| | | public void testSimpleLDAPConnectionFactorySimpleBind() throws LdapException { |
| | | final LDAPConnectionFactory factory = |
| | | newLDAPConnectionFactory("localhost", getListenPort()); |
| | | new LDAPConnectionFactory("localhost", |
| | | Integer.valueOf(CONFIG_PROPERTIES.getProperty("listen-port"))); |
| | | Connection connection = null; |
| | | try { |
| | | connection = factory.getConnection(); |
| | |
| | | @Test |
| | | public void testLDAPSASLBind() throws NumberFormatException, GeneralSecurityException, LdapException { |
| | | LDAPConnectionFactory factory = |
| | | newLDAPConnectionFactory("localhost", getListenPort()); |
| | | new LDAPConnectionFactory("localhost", |
| | | Integer.valueOf(CONFIG_PROPERTIES.getProperty("listen-port"))); |
| | | |
| | | Connection connection = factory.getConnection(); |
| | | PlainSASLBindRequest request = |
| | |
| | | final DeleteRequest deleteRequest = Requests.newDeleteRequest("sn=babs,dc=example,dc=org"); |
| | | |
| | | // LDAP Connection |
| | | final LDAPConnectionFactory factory = newLDAPConnectionFactory("localhost", getListenPort()); |
| | | final LDAPConnectionFactory factory = |
| | | new LDAPConnectionFactory("localhost", |
| | | Integer.valueOf(CONFIG_PROPERTIES.getProperty("listen-port"))); |
| | | Connection connection = null; |
| | | connection = factory.getConnection(); |
| | | connection.bind("cn=Directory Manager", "password".toCharArray()); |
| | |
| | | import org.forgerock.opendj.ldap.AuthorizationException; |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.ConnectionFactory; |
| | | import org.forgerock.opendj.ldap.Connections; |
| | | import org.forgerock.opendj.ldap.ConstraintViolationException; |
| | | import org.forgerock.opendj.ldap.DN; |
| | | import org.forgerock.opendj.ldap.DecodeException; |
| | |
| | | import org.forgerock.opendj.ldap.Entry; |
| | | import org.forgerock.opendj.ldap.EntryNotFoundException; |
| | | import org.forgerock.opendj.ldap.LDAPConnectionFactory; |
| | | import org.forgerock.opendj.ldap.LDAPOptions; |
| | | import org.forgerock.opendj.ldap.LdapException; |
| | | import org.forgerock.opendj.ldap.LinkedHashMapEntry; |
| | | import org.forgerock.opendj.ldap.ModificationType; |
| | |
| | | import org.testng.annotations.DataProvider; |
| | | import org.testng.annotations.Test; |
| | | |
| | | import static java.util.concurrent.TimeUnit.*; |
| | | |
| | | import static org.fest.assertions.Assertions.*; |
| | | import static org.fest.assertions.Fail.*; |
| | | import static org.forgerock.opendj.adapter.server3x.Adapters.*; |
| | | import static org.forgerock.opendj.adapter.server3x.EmbeddedServerTestCaseUtils.*; |
| | | import static org.forgerock.opendj.ldap.Connections.*; |
| | | import static org.forgerock.opendj.ldap.requests.Requests.*; |
| | | |
| | | /** |
| | | * This class defines a set of tests for the Adapters.class. |
| | |
| | | @Test |
| | | public class AdaptersTestCase extends ForgeRockTestCase { |
| | | |
| | | /** The timeout after which a connection will be marked as failed. */ |
| | | private static final long TIMEOUT_SEC = 3; |
| | | |
| | | private Integer getListenPort() { |
| | | return Integer.valueOf(CONFIG_PROPERTIES.getProperty("listen-port")); |
| | | } |
| | | |
| | | /** |
| | | * Provides an anonymous connection factories. |
| | | * |
| | |
| | | @DataProvider |
| | | public Object[][] anonymousConnectionFactories() { |
| | | return new Object[][] { |
| | | { newLDAPConnectionFactory("localhost", getListenPort()) }, |
| | | { newAnonymousConnectionFactory() } }; |
| | | { new LDAPConnectionFactory("localhost", |
| | | Integer.valueOf(CONFIG_PROPERTIES.getProperty("listen-port"))) }, |
| | | { Adapters.newAnonymousConnectionFactory() } }; |
| | | } |
| | | |
| | | /** |
| | |
| | | @DataProvider |
| | | public Object[][] rootConnectionFactories() { |
| | | return new Object[][] { |
| | | { newLDAPConnectionFactory( |
| | | "localhost", |
| | | getListenPort(), |
| | | new LDAPOptions() |
| | | .setBindRequest(newSimpleBindRequest("cn=directory manager", "password".toCharArray())) |
| | | .setTimeout(TIMEOUT_SEC, SECONDS)) }, |
| | | { newConnectionFactoryForUser(DN.valueOf("cn=directory manager")) } }; |
| | | { Connections.newAuthenticatedConnectionFactory( |
| | | new LDAPConnectionFactory("localhost", |
| | | Integer.valueOf(CONFIG_PROPERTIES.getProperty("listen-port"))), |
| | | Requests.newSimpleBindRequest("cn=directory manager", "password".toCharArray())) }, |
| | | { Adapters.newConnectionFactoryForUser(DN.valueOf("cn=directory manager")) } }; |
| | | } |
| | | |
| | | /** |
| | |
| | | @Test |
| | | public void testSimpleLDAPConnectionFactorySimpleBind() throws LdapException { |
| | | final LDAPConnectionFactory factory = |
| | | newLDAPConnectionFactory("localhost", getListenPort()); |
| | | new LDAPConnectionFactory("localhost", |
| | | Integer.valueOf(CONFIG_PROPERTIES.getProperty("listen-port"))); |
| | | Connection connection = null; |
| | | try { |
| | | connection = factory.getConnection(); |
| | |
| | | @Test |
| | | public void testLDAPSASLBind() throws NumberFormatException, GeneralSecurityException, LdapException { |
| | | LDAPConnectionFactory factory = |
| | | newLDAPConnectionFactory("localhost", getListenPort()); |
| | | new LDAPConnectionFactory("localhost", |
| | | Integer.valueOf(CONFIG_PROPERTIES.getProperty("listen-port"))); |
| | | |
| | | Connection connection = factory.getConnection(); |
| | | PlainSASLBindRequest request = |
| | |
| | | final DeleteRequest deleteRequest = Requests.newDeleteRequest("sn=babs,dc=example,dc=org"); |
| | | |
| | | // LDAP Connection |
| | | final LDAPConnectionFactory factory = newLDAPConnectionFactory("localhost", getListenPort()); |
| | | final LDAPConnectionFactory factory = |
| | | new LDAPConnectionFactory("localhost", |
| | | Integer.valueOf(CONFIG_PROPERTIES.getProperty("listen-port"))); |
| | | Connection connection = null; |
| | | connection = factory.getConnection(); |
| | | connection.bind("cn=Directory Manager", "password".toCharArray()); |