From 6399a7ab74ed377b0644e2b0c63e5183796ef22c Mon Sep 17 00:00:00 2001
From: Gaetan Boismal <gaetan.boismal@forgerock.com>
Date: Thu, 27 Nov 2014 16:05:26 +0000
Subject: [PATCH] OPENDJ-1607 (CR-5295) Merge HeartBeatConnectionFactory, AuthenticatedConnectionFactory and GrizzlyLDAPConnectionFactory
---
opendj-sdk/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnection.java | 759 ++++++----------------------------------------------------
1 files changed, 83 insertions(+), 676 deletions(-)
diff --git a/opendj-sdk/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnection.java b/opendj-sdk/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnection.java
index e332639..b59f093 100644
--- a/opendj-sdk/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnection.java
+++ b/opendj-sdk/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnection.java
@@ -21,60 +21,37 @@
* CDDL HEADER END
*
*
- * Copyright 2010 Sun Microsystems, Inc.
- * Portions Copyright 2011-2014 ForgeRock AS
+ * Copyright 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.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.Request;
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;
@@ -85,12 +62,7 @@
import static org.forgerock.opendj.ldap.LdapException.*;
import static org.forgerock.opendj.ldap.spi.LdapPromises.*;
-import static com.forgerock.opendj.grizzly.GrizzlyMessages.*;
-
-/**
- * LDAP connection implementation.
- */
-final class GrizzlyLDAPConnection extends AbstractAsynchronousConnection implements TimeoutEventListener {
+final class GrizzlyLDAPConnection extends AbstractLdapConnectionImpl<GrizzlyLDAPConnectionFactory> {
/**
* A dummy SSL client engine configurator as SSLFilter only needs client
* config. This prevents Grizzly from needlessly using JVM defaults which
@@ -99,637 +71,87 @@
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;
- /**
- * 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) {
+ @SuppressWarnings("rawtypes")
+ public GrizzlyLDAPConnection(final org.glassfish.grizzly.Connection connection,
+ final GrizzlyLDAPConnectionFactory attachedFactory) {
+ super(attachedFactory);
this.connection = connection;
- this.factory = factory;
}
@Override
- 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) {
+ protected LdapPromise<Void> abandonAsync0(final int messageID, 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(adaptRequestIOException(e));
+ return newFailedLdapPromise(newLdapException(adaptRequestIOException(e)));
} finally {
GrizzlyUtils.recycleWriter(writer);
}
}
@Override
- 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);
+ protected void bindAsync0(final int messageID, final BindRequest request, final BindClient bindClient,
+ final IntermediateResponseHandler intermediateResponseHandler) throws LdapException {
+ checkConnectionIsValid();
+ final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter();
try {
- 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();
+ // 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);
}
}
@Override
- public LdapPromise<BindResult> bindAsync(final BindRequest request,
- final IntermediateResponseHandler intermediateResponseHandler) {
- final int messageID = nextMsgID.getAndIncrement();
- final BindClient context;
+ 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();
try {
- context = request.createBindClient(Connections.getHostString(factory.getSocketAddress()));
- } catch (final LdapException e) {
- return newFailedLdapPromise(e, messageID);
+ 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);
}
+ getFactory().getTimeoutChecker().removeListener(this);
+ connection.closeSilently();
+ }
- final BindResultLdapPromiseImpl promise =
- newBindLdapPromise(messageID, request, context, intermediateResponseHandler, this);
-
+ @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();
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
- 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 {
- 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);
- }
+ requestWriter.writeRequest(messageID, writer, request);
+ connection.write(writer.getASN1Writer().getBuffer(), null);
+ } catch (final IOException e) {
+ removePendingResult(messageID).setResultOrError(adaptRequestIOException(e));
+ } finally {
+ GrizzlyUtils.recycleWriter(writer);
}
}
@@ -741,19 +163,14 @@
* The filter to be installed.
*/
void installFilter(final Filter filter) {
- synchronized (stateLock) {
+ synchronized (state) {
GrizzlyUtils.addFilterToConnection(filter, connection);
}
}
- /**
- * 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) {
+ @Override
+ protected boolean isTLSEnabled() {
+ synchronized (state) {
final FilterChain currentFilterChain = (FilterChain) connection.getProcessor();
for (final Filter filter : currentFilterChain) {
if (filter instanceof SSLFilter) {
@@ -764,17 +181,9 @@
}
}
- 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 (stateLock) {
+ synchronized (state) {
if (isTLSEnabled()) {
throw new IllegalStateException("TLS already enabled");
}
@@ -791,43 +200,41 @@
}
}
- private LdapException adaptRequestIOException(final IOException e) {
+ private Result 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(errorResult);
- return newLdapException(errorResult);
+ connectionErrorOccurred(false, errorResult);
+ return errorResult;
}
- private void checkBindOrStartTLSInProgress() throws LdapException {
- if (bindOrStartTLSInProgress.get()) {
- throw newLdapException(ResultCode.OPERATIONS_ERROR, "Bind or Start TLS operation in progress");
- }
+ @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 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 connectionErrorOccurred(boolean isDisconnectNotification, Result reason) {
+ super.connectionErrorOccurred(isDisconnectNotification, reason);
}
- private void connectionErrorOccurred(final Result reason) {
- close(null, false, reason);
+ @Override
+ protected void handleUnsolicitedNotification(final ExtendedResult notification) {
+ super.handleUnsolicitedNotification(notification);
}
- private boolean isValid0() {
- return !isFailed && !isClosed;
+ int continuePendingBindRequest(BindResultLdapPromiseImpl promise)
+ throws LdapException {
+ final int newMsgID = newMessageID();
+ checkConnectionIsValid();
+ addPendingResult(newMsgID, promise);
+
+ return newMsgID;
}
}
--
Gitblit v1.10.0