From a4e2fc0298e8d60aa0e4bcfd3304303d952e0972 Mon Sep 17 00:00:00 2001
From: Gaetan Boismal <gaetan.boismal@forgerock.com>
Date: Fri, 28 Nov 2014 14:52:21 +0000
Subject: [PATCH] OPENDJ-1607 Revert changes to revision 11339
---
opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnectionFactory.java | 294 ++++++++++++++++++++++++++++++++++++++++++----------------
1 files changed, 210 insertions(+), 84 deletions(-)
diff --git a/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnectionFactory.java b/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnectionFactory.java
index 9a1f278..8df6801 100644
--- a/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnectionFactory.java
+++ b/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnectionFactory.java
@@ -28,8 +28,11 @@
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;
@@ -40,12 +43,15 @@
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;
@@ -64,21 +70,19 @@
/**
* 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;
@@ -86,11 +90,68 @@
}
@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);
+ }
}
}
@@ -99,6 +160,51 @@
// 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
@@ -122,24 +228,25 @@
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
@@ -155,15 +262,6 @@
*/
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);
}
/**
@@ -183,60 +281,88 @@
* 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");
+ }
}
-
}
--
Gitblit v1.10.0