| | |
| | | |
| | | package org.forgerock.opendj.grizzly; |
| | | |
| | | import static com.forgerock.opendj.grizzly.GrizzlyMessages.LDAP_CONNECTION_CONNECT_TIMEOUT; |
| | | import static org.forgerock.opendj.grizzly.DefaultTCPNIOTransport.DEFAULT_TRANSPORT; |
| | | import static org.forgerock.opendj.grizzly.GrizzlyUtils.buildFilterChain; |
| | | import static org.forgerock.opendj.grizzly.GrizzlyUtils.configureConnection; |
| | | import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult; |
| | | import static org.forgerock.opendj.ldap.TimeoutChecker.TIMEOUT_CHECKER; |
| | | |
| | |
| | | |
| | | import javax.net.ssl.SSLEngine; |
| | | |
| | | import org.forgerock.i18n.slf4j.LocalizedLogger; |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.ErrorResultException; |
| | | import org.forgerock.opendj.ldap.FutureResult; |
| | |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.ResultHandler; |
| | | import org.forgerock.opendj.ldap.TimeoutChecker; |
| | | import org.forgerock.opendj.ldap.TimeoutEventListener; |
| | | import org.forgerock.opendj.ldap.requests.Requests; |
| | | import org.forgerock.opendj.ldap.requests.StartTLSExtendedRequest; |
| | | import org.forgerock.opendj.ldap.responses.ExtendedResult; |
| | |
| | | * LDAP connection factory implementation using Grizzly for transport. |
| | | */ |
| | | public final class GrizzlyLDAPConnectionFactory implements LDAPConnectionFactoryImpl { |
| | | private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); |
| | | |
| | | /** |
| | | * Adapts a Grizzly connection completion handler to an LDAP connection |
| | | * asynchronous future result. |
| | | */ |
| | | @SuppressWarnings("rawtypes") |
| | | private final class CompletionHandlerAdapter implements |
| | | CompletionHandler<org.glassfish.grizzly.Connection> { |
| | | |
| | | CompletionHandler<org.glassfish.grizzly.Connection>, TimeoutEventListener { |
| | | private final AsynchronousFutureResult<Connection, ResultHandler<? super Connection>> future; |
| | | private final long timeoutEndTime; |
| | | |
| | | private CompletionHandlerAdapter( |
| | | final AsynchronousFutureResult<Connection, ResultHandler<? super Connection>> future) { |
| | | this.future = future; |
| | | final long timeoutMS = getTimeout(); |
| | | this.timeoutEndTime = timeoutMS > 0 ? System.currentTimeMillis() + timeoutMS : 0; |
| | | timeoutChecker.get().addListener(this); |
| | | } |
| | | |
| | | @Override |
| | |
| | | |
| | | // Start TLS or install SSL layer asynchronously. |
| | | |
| | | // Give up immediately if the future has been cancelled. |
| | | if (future.isCancelled()) { |
| | | // Give up immediately if the future has been cancelled or timed out. |
| | | if (future.isDone()) { |
| | | timeoutChecker.get().removeListener(this); |
| | | connection.close(); |
| | | releaseTransportAndTimeoutChecker(); |
| | | return; |
| | | } |
| | | |
| | |
| | | @Override |
| | | public void failed(final Throwable throwable) { |
| | | // Adapt and forward. |
| | | timeoutChecker.get().removeListener(this); |
| | | future.handleErrorResult(adaptConnectionException(throwable)); |
| | | releaseTransportAndTimeoutChecker(); |
| | | } |
| | |
| | | // Ignore this. |
| | | } |
| | | |
| | | private GrizzlyLDAPConnection adaptConnection(final org.glassfish.grizzly.Connection<?> connection) { |
| | | /* |
| | | * Test shows that its much faster with non block writes but risk |
| | | * running out of memory if the server is slow. |
| | | */ |
| | | connection.configureBlocking(true); |
| | | private GrizzlyLDAPConnection adaptConnection( |
| | | final org.glassfish.grizzly.Connection<?> connection) { |
| | | configureConnection(connection, options.isTCPNoDelay(), options.isKeepAlive(), options |
| | | .isReuseAddress(), options.getLinger(), logger); |
| | | |
| | | final GrizzlyLDAPConnection ldapConnection = |
| | | new GrizzlyLDAPConnection(connection, GrizzlyLDAPConnectionFactory.this); |
| | | if (options.getTimeout(TimeUnit.MILLISECONDS) > 0) { |
| | | timeoutChecker.get().addListener(ldapConnection); |
| | | } |
| | | timeoutChecker.get().addListener(ldapConnection); |
| | | clientFilter.registerConnection(connection, ldapConnection); |
| | | return ldapConnection; |
| | | } |
| | |
| | | |
| | | private void onFailure(final GrizzlyLDAPConnection connection, final Throwable t) { |
| | | // Abort connection attempt due to error. |
| | | connection.close(); |
| | | timeoutChecker.get().removeListener(this); |
| | | future.handleErrorResult(adaptConnectionException(t)); |
| | | releaseTransportAndTimeoutChecker(); |
| | | connection.close(); |
| | | } |
| | | |
| | | private void onSuccess(final GrizzlyLDAPConnection connection) { |
| | | future.handleResult(connection); |
| | | |
| | | // Close the connection if the future was cancelled. |
| | | if (future.isCancelled()) { |
| | | timeoutChecker.get().removeListener(this); |
| | | if (!future.tryHandleResult(connection)) { |
| | | // The connection has been either cancelled or it has timed out. |
| | | connection.close(); |
| | | releaseTransportAndTimeoutChecker(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public long handleTimeout(final long currentTime) { |
| | | if (timeoutEndTime == 0) { |
| | | return 0; |
| | | } else if (timeoutEndTime > currentTime) { |
| | | return timeoutEndTime - currentTime; |
| | | } else { |
| | | future.handleErrorResult(newErrorResult(ResultCode.CLIENT_SIDE_TIMEOUT, |
| | | LDAP_CONNECTION_CONNECT_TIMEOUT.get(socketAddress, getTimeout()).toString())); |
| | | return 0; |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public long getTimeout() { |
| | | return options.getConnectTimeout(TimeUnit.MILLISECONDS); |
| | | } |
| | | } |
| | | |
| | | private final LDAPClientFilter clientFilter; |
| | |
| | | .acquire(); |
| | | |
| | | /** |
| | | * Creates a new LDAP connection factory based on Grizzly which can be used to |
| | | * create connections to the Directory Server at the provided host and port |
| | | * address using provided connection options. |
| | | * Creates a new LDAP connection factory based on Grizzly which can be used |
| | | * to create connections to the Directory Server at the provided host and |
| | | * port address using provided connection options. |
| | | * |
| | | * @param address |
| | | * The address of the Directory Server to connect to. |
| | |
| | | this.options = new LDAPOptions(options); |
| | | this.clientFilter = new LDAPClientFilter(this.options.getDecodeOptions(), 0); |
| | | this.defaultFilterChain = |
| | | GrizzlyUtils.buildFilterChain(this.transport.get().getProcessor(), clientFilter); |
| | | |
| | | buildFilterChain(this.transport.get().getProcessor(), clientFilter); |
| | | } |
| | | |
| | | @Override |
| | |
| | | .build(); |
| | | final AsynchronousFutureResult<Connection, ResultHandler<? super Connection>> future = |
| | | new AsynchronousFutureResult<Connection, ResultHandler<? super Connection>>(handler); |
| | | final CompletionHandlerAdapter cha = new CompletionHandlerAdapter(future); |
| | | connectorHandler.connect(socketAddress, cha); |
| | | connectorHandler.connect(socketAddress, new CompletionHandlerAdapter(future)); |
| | | return future; |
| | | } |
| | | |