mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Matthew Swift
25.33.2012 263d085885df024dca9250cc03c807912b0a7662
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/RequestHandlerFactoryAdapter.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * 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
 * trunk/opendj3/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:
 * 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
@@ -27,8 +26,6 @@
package org.forgerock.opendj.ldap;
import static org.forgerock.opendj.ldap.CoreMessages.*;
import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
@@ -39,918 +36,744 @@
import java.util.concurrent.atomic.AtomicBoolean;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.opendj.ldap.requests.*;
import org.forgerock.opendj.ldap.responses.*;
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.CancelExtendedRequest;
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.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.responses.SearchResultEntry;
import org.forgerock.opendj.ldap.responses.SearchResultReference;
import com.forgerock.opendj.util.Validator;
/**
 * An adapter which converts a {@code RequestHandlerFactory} into a
 * {@code ServerConnectionFactory}.
 *
 * @param <C>
 *          The type of client context.
 *            The type of client context.
 */
final class RequestHandlerFactoryAdapter<C> implements
    ServerConnectionFactory<C, Integer>
{
  /**
   * Request context implementation.
   */
  private static class RequestContextImpl<S extends Result, H extends ResultHandler<? super S>>
      implements RequestContext, ResultHandler<S>
  {
    // Adapter class which invokes cancel result handlers with correct result
    // type.
    private static final class ExtendedResultHandlerHolder<R extends ExtendedResult>
    {
      private final ExtendedRequest<R> request;
      private final ResultHandler<? super R> resultHandler;
      private ExtendedResultHandlerHolder(final ExtendedRequest<R> request,
          final ResultHandler<? super R> resultHandler)
      {
        this.request = request;
        this.resultHandler = resultHandler;
      }
      private void handleSuccess()
      {
        final R cancelResult = request.getResultDecoder()
            .newExtendedErrorResult(ResultCode.SUCCESS, "", "");
        resultHandler.handleResult(cancelResult);
      }
      private void handleTooLate()
      {
        final R cancelResult = request.getResultDecoder()
            .newExtendedErrorResult(ResultCode.TOO_LATE, "", "");
        resultHandler.handleErrorResult(ErrorResultException
            .newErrorResult(cancelResult));
      }
    }
    private static enum RequestState
    {
      // Request active
      PENDING,
      // Request active, cancel requested
      CANCEL_REQUESTED,
      // Request active, too late to cancel
      TOO_LATE,
      // Result sent, not cancelled
      RESULT_SENT,
      // Result sent, was cancelled
      CANCELLED;
    }
    private final int messageID;
    // Cancellation state guarded by lock.
    private final Object stateLock = new Object();
    // These should be notified when a cancel request arrives, at most once.
    private List<CancelRequestListener> cancelRequestListeners = null;
    // These should be notified when the result is set.
    private List<ExtendedResultHandlerHolder<?>> cancelResultHandlers = null;
    private RequestState state = RequestState.PENDING;
    private LocalizableMessage cancelRequestReason = null;
    private boolean sendResult = true;
    private final boolean isCancelSupported;
    private final ServerConnectionImpl<?> clientConnection;
    protected final H resultHandler;
    protected RequestContextImpl(
        final ServerConnectionImpl<?> clientConnection, final H resultHandler,
        final int messageID, final boolean isCancelSupported)
    {
      this.clientConnection = clientConnection;
      this.resultHandler = resultHandler;
      this.messageID = messageID;
      this.isCancelSupported = isCancelSupported;
    }
final class RequestHandlerFactoryAdapter<C> implements ServerConnectionFactory<C, Integer> {
    /**
     * {@inheritDoc}
     * Request context implementation.
     */
    @Override
    public void addCancelRequestListener(final CancelRequestListener listener)
    {
      Validator.ensureNotNull(listener);
    private static class RequestContextImpl<S extends Result, H extends ResultHandler<? super S>>
            implements RequestContext, ResultHandler<S> {
      boolean invokeImmediately = false;
      synchronized (stateLock)
      {
        switch (state)
        {
        case PENDING:
          if (cancelRequestListeners == null)
          {
            cancelRequestListeners = new LinkedList<CancelRequestListener>();
          }
          cancelRequestListeners.add(listener);
          break;
        case CANCEL_REQUESTED:
          // Signal immediately outside lock.
          invokeImmediately = true;
          break;
        case TOO_LATE:
        case RESULT_SENT:
        case CANCELLED:
          // No point in registering the callback since the request can never be
          // cancelled now.
          break;
        }
      }
        // Adapter class which invokes cancel result handlers with correct
        // result
        // type.
        private static final class ExtendedResultHandlerHolder<R extends ExtendedResult> {
            private final ExtendedRequest<R> request;
            private final ResultHandler<? super R> resultHandler;
      if (invokeImmediately)
      {
        listener.handleCancelRequest(cancelRequestReason);
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void checkIfCancelled(final boolean signalTooLate)
        throws CancelledResultException
    {
      synchronized (stateLock)
      {
        switch (state)
        {
        case PENDING:
          // No cancel request, so no handlers, just switch state.
          if (signalTooLate)
          {
            cancelRequestListeners = null;
            state = RequestState.TOO_LATE;
          }
          break;
        case CANCEL_REQUESTED:
          // Don't change state: let the handler ack the cancellation request.
          throw (CancelledResultException) newErrorResult(ResultCode.CANCELLED,
              cancelRequestReason.toString());
        case TOO_LATE:
          // Already too late. Nothing to do.
          break;
        case RESULT_SENT:
        case CANCELLED:
          // This should not happen - could throw an illegal state exception?
          break;
        }
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public int getMessageID()
    {
      return messageID;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleErrorResult(final ErrorResultException error)
    {
      if (clientConnection.removePendingRequest(this))
      {
        if (setResult(error.getResult()))
        {
          // FIXME: we must invoke the result handler even when abandoned so
          // that chained result handlers may clean up, log, etc. We really need
          // to signal that the result must not be sent to the client.
        }
        resultHandler.handleErrorResult(error);
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleResult(final S result)
    {
      if (clientConnection.removePendingRequest(this))
      {
        if (setResult(result))
        {
          // FIXME: we must invoke the result handler even when abandoned so
          // that chained result handlers may clean up, log, etc. We really need
          // to signal that the result must not be sent to the client.
        }
        resultHandler.handleResult(result);
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void removeCancelRequestListener(final CancelRequestListener listener)
    {
      Validator.ensureNotNull(listener);
      synchronized (stateLock)
      {
        if (cancelRequestListeners != null)
        {
          cancelRequestListeners.remove(listener);
        }
      }
    }
    private <R extends ExtendedResult> void cancel(
        final LocalizableMessage reason,
        final ExtendedRequest<R> cancelRequest,
        final ResultHandler<? super R> cancelResultHandler,
        final boolean sendResult)
    {
      Validator.ensureNotNull(reason);
      if (!isCancelSupported)
      {
        if (cancelResultHandler != null)
        {
          final Result result = Responses
              .newGenericExtendedResult(ResultCode.CANNOT_CANCEL);
          cancelResultHandler.handleErrorResult(newErrorResult(result));
        }
        return;
      }
      List<CancelRequestListener> tmpListeners = null;
      boolean invokeResultHandler = false;
      boolean resultHandlerIsSuccess = false;
      synchronized (stateLock)
      {
        switch (state)
        {
        case PENDING:
          // Switch to CANCEL_REQUESTED state.
          cancelRequestReason = reason;
          if (cancelResultHandler != null)
          {
            cancelResultHandlers = new LinkedList<ExtendedResultHandlerHolder<?>>();
            cancelResultHandlers.add(new ExtendedResultHandlerHolder<R>(
                cancelRequest, cancelResultHandler));
          }
          tmpListeners = cancelRequestListeners;
          cancelRequestListeners = null;
          state = RequestState.CANCEL_REQUESTED;
          this.sendResult &= sendResult;
          break;
        case CANCEL_REQUESTED:
          // Cancel already request so listeners already invoked.
          if (cancelResultHandler != null)
          {
            if (cancelResultHandlers == null)
            {
              cancelResultHandlers = new LinkedList<ExtendedResultHandlerHolder<?>>();
            private ExtendedResultHandlerHolder(final ExtendedRequest<R> request,
                    final ResultHandler<? super R> resultHandler) {
                this.request = request;
                this.resultHandler = resultHandler;
            }
            cancelResultHandlers.add(new ExtendedResultHandlerHolder<R>(
                cancelRequest, cancelResultHandler));
          }
          break;
        case TOO_LATE:
        case RESULT_SENT:
          // Cannot cancel, so invoke result handler immediately outside of
          // lock.
          if (cancelResultHandler != null)
          {
            invokeResultHandler = true;
            resultHandlerIsSuccess = false;
          }
          break;
        case CANCELLED:
          // Multiple cancellation attempts. Clients should not do this, but the
          // cancel will effectively succeed immediately, so invoke result
          // handler immediately outside of lock.
          if (cancelResultHandler != null)
          {
            invokeResultHandler = true;
            resultHandlerIsSuccess = true;
          }
          break;
        }
      }
      // Invoke listeners outside of lock.
      if (tmpListeners != null)
      {
        for (final CancelRequestListener listener : tmpListeners)
        {
          listener.handleCancelRequest(reason);
        }
      }
            private void handleSuccess() {
                final R cancelResult =
                        request.getResultDecoder().newExtendedErrorResult(ResultCode.SUCCESS, "",
                                "");
                resultHandler.handleResult(cancelResult);
            }
      if (invokeResultHandler)
      {
        if (resultHandlerIsSuccess)
        {
          final R result = cancelRequest.getResultDecoder()
              .newExtendedErrorResult(ResultCode.SUCCESS, "", "");
          cancelResultHandler.handleResult(result);
            private void handleTooLate() {
                final R cancelResult =
                        request.getResultDecoder().newExtendedErrorResult(ResultCode.TOO_LATE, "",
                                "");
                resultHandler.handleErrorResult(ErrorResultException.newErrorResult(cancelResult));
            }
        }
        else
        {
          final Result result = Responses
              .newGenericExtendedResult(ResultCode.TOO_LATE);
          cancelResultHandler.handleErrorResult(ErrorResultException
              .newErrorResult(result));
        private static enum RequestState {
            // Request active
            PENDING,
            // Request active, cancel requested
            CANCEL_REQUESTED,
            // Request active, too late to cancel
            TOO_LATE,
            // Result sent, not cancelled
            RESULT_SENT,
            // Result sent, was cancelled
            CANCELLED;
        }
      }
        private final int messageID;
        // Cancellation state guarded by lock.
        private final Object stateLock = new Object();
        // These should be notified when a cancel request arrives, at most once.
        private List<CancelRequestListener> cancelRequestListeners = null;
        // These should be notified when the result is set.
        private List<ExtendedResultHandlerHolder<?>> cancelResultHandlers = null;
        private RequestState state = RequestState.PENDING;
        private LocalizableMessage cancelRequestReason = null;
        private boolean sendResult = true;
        private final boolean isCancelSupported;
        private final ServerConnectionImpl<?> clientConnection;
        protected final H resultHandler;
        protected RequestContextImpl(final ServerConnectionImpl<?> clientConnection,
                final H resultHandler, final int messageID, final boolean isCancelSupported) {
            this.clientConnection = clientConnection;
            this.resultHandler = resultHandler;
            this.messageID = messageID;
            this.isCancelSupported = isCancelSupported;
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void addCancelRequestListener(final CancelRequestListener listener) {
            Validator.ensureNotNull(listener);
            boolean invokeImmediately = false;
            synchronized (stateLock) {
                switch (state) {
                case PENDING:
                    if (cancelRequestListeners == null) {
                        cancelRequestListeners = new LinkedList<CancelRequestListener>();
                    }
                    cancelRequestListeners.add(listener);
                    break;
                case CANCEL_REQUESTED:
                    // Signal immediately outside lock.
                    invokeImmediately = true;
                    break;
                case TOO_LATE:
                case RESULT_SENT:
                case CANCELLED:
                    // No point in registering the callback since the request
                    // can never be
                    // cancelled now.
                    break;
                }
            }
            if (invokeImmediately) {
                listener.handleCancelRequest(cancelRequestReason);
            }
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void checkIfCancelled(final boolean signalTooLate) throws CancelledResultException {
            synchronized (stateLock) {
                switch (state) {
                case PENDING:
                    // No cancel request, so no handlers, just switch state.
                    if (signalTooLate) {
                        cancelRequestListeners = null;
                        state = RequestState.TOO_LATE;
                    }
                    break;
                case CANCEL_REQUESTED:
                    // Don't change state: let the handler ack the cancellation
                    // request.
                    throw (CancelledResultException) newErrorResult(ResultCode.CANCELLED,
                            cancelRequestReason.toString());
                case TOO_LATE:
                    // Already too late. Nothing to do.
                    break;
                case RESULT_SENT:
                case CANCELLED:
                    // This should not happen - could throw an illegal state
                    // exception?
                    break;
                }
            }
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public int getMessageID() {
            return messageID;
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleErrorResult(final ErrorResultException error) {
            if (clientConnection.removePendingRequest(this)) {
                if (setResult(error.getResult())) {
                    // FIXME: we must invoke the result handler even when
                    // abandoned so
                    // that chained result handlers may clean up, log, etc. We
                    // really need
                    // to signal that the result must not be sent to the client.
                }
                resultHandler.handleErrorResult(error);
            }
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleResult(final S result) {
            if (clientConnection.removePendingRequest(this)) {
                if (setResult(result)) {
                    // FIXME: we must invoke the result handler even when
                    // abandoned so
                    // that chained result handlers may clean up, log, etc. We
                    // really need
                    // to signal that the result must not be sent to the client.
                }
                resultHandler.handleResult(result);
            }
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void removeCancelRequestListener(final CancelRequestListener listener) {
            Validator.ensureNotNull(listener);
            synchronized (stateLock) {
                if (cancelRequestListeners != null) {
                    cancelRequestListeners.remove(listener);
                }
            }
        }
        private <R extends ExtendedResult> void cancel(final LocalizableMessage reason,
                final ExtendedRequest<R> cancelRequest,
                final ResultHandler<? super R> cancelResultHandler, final boolean sendResult) {
            Validator.ensureNotNull(reason);
            if (!isCancelSupported) {
                if (cancelResultHandler != null) {
                    final Result result =
                            Responses.newGenericExtendedResult(ResultCode.CANNOT_CANCEL);
                    cancelResultHandler.handleErrorResult(newErrorResult(result));
                }
                return;
            }
            List<CancelRequestListener> tmpListeners = null;
            boolean invokeResultHandler = false;
            boolean resultHandlerIsSuccess = false;
            synchronized (stateLock) {
                switch (state) {
                case PENDING:
                    // Switch to CANCEL_REQUESTED state.
                    cancelRequestReason = reason;
                    if (cancelResultHandler != null) {
                        cancelResultHandlers = new LinkedList<ExtendedResultHandlerHolder<?>>();
                        cancelResultHandlers.add(new ExtendedResultHandlerHolder<R>(cancelRequest,
                                cancelResultHandler));
                    }
                    tmpListeners = cancelRequestListeners;
                    cancelRequestListeners = null;
                    state = RequestState.CANCEL_REQUESTED;
                    this.sendResult &= sendResult;
                    break;
                case CANCEL_REQUESTED:
                    // Cancel already request so listeners already invoked.
                    if (cancelResultHandler != null) {
                        if (cancelResultHandlers == null) {
                            cancelResultHandlers = new LinkedList<ExtendedResultHandlerHolder<?>>();
                        }
                        cancelResultHandlers.add(new ExtendedResultHandlerHolder<R>(cancelRequest,
                                cancelResultHandler));
                    }
                    break;
                case TOO_LATE:
                case RESULT_SENT:
                    // Cannot cancel, so invoke result handler immediately
                    // outside of
                    // lock.
                    if (cancelResultHandler != null) {
                        invokeResultHandler = true;
                        resultHandlerIsSuccess = false;
                    }
                    break;
                case CANCELLED:
                    // Multiple cancellation attempts. Clients should not do
                    // this, but the
                    // cancel will effectively succeed immediately, so invoke
                    // result
                    // handler immediately outside of lock.
                    if (cancelResultHandler != null) {
                        invokeResultHandler = true;
                        resultHandlerIsSuccess = true;
                    }
                    break;
                }
            }
            // Invoke listeners outside of lock.
            if (tmpListeners != null) {
                for (final CancelRequestListener listener : tmpListeners) {
                    listener.handleCancelRequest(reason);
                }
            }
            if (invokeResultHandler) {
                if (resultHandlerIsSuccess) {
                    final R result =
                            cancelRequest.getResultDecoder().newExtendedErrorResult(
                                    ResultCode.SUCCESS, "", "");
                    cancelResultHandler.handleResult(result);
                } else {
                    final Result result = Responses.newGenericExtendedResult(ResultCode.TOO_LATE);
                    cancelResultHandler.handleErrorResult(ErrorResultException
                            .newErrorResult(result));
                }
            }
        }
        /**
         * Sets the result associated with this request context and updates the
         * state accordingly.
         *
         * @param result
         *            The result.
         */
        private boolean setResult(final Result result) {
            List<ExtendedResultHandlerHolder<?>> tmpHandlers = null;
            boolean isCancelled = false;
            boolean maySendResult;
            synchronized (stateLock) {
                maySendResult = sendResult;
                switch (state) {
                case PENDING:
                case TOO_LATE:
                    // Switch to appropriate final state.
                    if (!result.getResultCode().equals(ResultCode.CANCELLED)) {
                        state = RequestState.RESULT_SENT;
                    } else {
                        state = RequestState.CANCELLED;
                    }
                    break;
                case CANCEL_REQUESTED:
                    // Switch to appropriate final state and invoke any cancel
                    // request
                    // handlers.
                    if (!result.getResultCode().equals(ResultCode.CANCELLED)) {
                        state = RequestState.RESULT_SENT;
                    } else {
                        state = RequestState.CANCELLED;
                    }
                    isCancelled = (state == RequestState.CANCELLED);
                    tmpHandlers = cancelResultHandlers;
                    cancelResultHandlers = null;
                    break;
                case RESULT_SENT:
                case CANCELLED:
                    // This should not happen - could throw an illegal state
                    // exception?
                    maySendResult = false; // Prevent sending multiple results.
                    break;
                }
            }
            // Invoke handlers outside of lock.
            if (tmpHandlers != null) {
                for (final ExtendedResultHandlerHolder<?> handler : tmpHandlers) {
                    if (isCancelled) {
                        handler.handleSuccess();
                    } else {
                        handler.handleTooLate();
                    }
                }
            }
            return maySendResult;
        }
    }
    /**
     * Search request context implementation.
     */
    private final static class SearchRequestContextImpl extends
            RequestContextImpl<Result, SearchResultHandler> implements SearchResultHandler {
        private SearchRequestContextImpl(final ServerConnectionImpl<?> clientConnection,
                final SearchResultHandler resultHandler, final int messageID,
                final boolean isCancelSupported) {
            super(clientConnection, resultHandler, messageID, isCancelSupported);
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public boolean handleEntry(final SearchResultEntry entry) {
            return resultHandler.handleEntry(entry);
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public boolean handleReference(final SearchResultReference reference) {
            return resultHandler.handleReference(reference);
        }
    }
    private static final class ServerConnectionImpl<C> implements ServerConnection<Integer> {
        private final RequestHandler<RequestContext> requestHandler;
        private final AtomicBoolean isClosed = new AtomicBoolean();
        private final ConcurrentHashMap<Integer, RequestContextImpl<?, ?>> pendingRequests =
                new ConcurrentHashMap<Integer, RequestContextImpl<?, ?>>();
        private ServerConnectionImpl(final RequestHandler<RequestContext> requestHandler) {
            this.requestHandler = requestHandler;
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleAbandon(final Integer messageID, final AbandonRequest request) {
            final RequestContextImpl<?, ?> abandonedRequest =
                    getPendingRequest(request.getRequestID());
            if (abandonedRequest != null) {
                final LocalizableMessage abandonReason =
                        INFO_CANCELED_BY_ABANDON_REQUEST.get(messageID);
                abandonedRequest.cancel(abandonReason, null, null, false);
            }
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleAdd(final Integer messageID, final AddRequest request,
                final IntermediateResponseHandler intermediateResponseHandler,
                final ResultHandler<? super Result> resultHandler) {
            final RequestContextImpl<Result, ResultHandler<? super Result>> requestContext =
                    new RequestContextImpl<Result, ResultHandler<? super Result>>(this,
                            resultHandler, messageID, true);
            if (addPendingRequest(requestContext)) {
                requestHandler.handleAdd(requestContext, request, intermediateResponseHandler,
                        requestContext);
            }
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleBind(final Integer messageID, final int version,
                final BindRequest request,
                final IntermediateResponseHandler intermediateResponseHandler,
                final ResultHandler<? super BindResult> resultHandler) {
            final RequestContextImpl<BindResult, ResultHandler<? super BindResult>> requestContext =
                    new RequestContextImpl<BindResult, ResultHandler<? super BindResult>>(this,
                            resultHandler, messageID, false);
            if (addPendingRequest(requestContext)) {
                requestHandler.handleBind(requestContext, version, request,
                        intermediateResponseHandler, requestContext);
            }
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleCompare(final Integer messageID, final CompareRequest request,
                final IntermediateResponseHandler intermediateResponseHandler,
                final ResultHandler<? super CompareResult> resultHandler) {
            final RequestContextImpl<CompareResult, ResultHandler<? super CompareResult>> requestContext =
                    new RequestContextImpl<CompareResult, ResultHandler<? super CompareResult>>(
                            this, resultHandler, messageID, true);
            if (addPendingRequest(requestContext)) {
                requestHandler.handleCompare(requestContext, request, intermediateResponseHandler,
                        requestContext);
            }
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleConnectionClosed(final Integer messageID, final UnbindRequest request) {
            final LocalizableMessage cancelReason = INFO_CANCELED_BY_CLIENT_DISCONNECT.get();
            doClose(cancelReason);
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleConnectionDisconnected(final ResultCode resultCode, final String message) {
            final LocalizableMessage cancelReason = INFO_CANCELED_BY_SERVER_DISCONNECT.get();
            doClose(cancelReason);
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleConnectionError(final Throwable error) {
            final LocalizableMessage cancelReason = INFO_CANCELED_BY_CLIENT_ERROR.get();
            doClose(cancelReason);
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleDelete(final Integer messageID, final DeleteRequest request,
                final IntermediateResponseHandler intermediateResponseHandler,
                final ResultHandler<? super Result> resultHandler) {
            final RequestContextImpl<Result, ResultHandler<? super Result>> requestContext =
                    new RequestContextImpl<Result, ResultHandler<? super Result>>(this,
                            resultHandler, messageID, true);
            if (addPendingRequest(requestContext)) {
                requestHandler.handleDelete(requestContext, request, intermediateResponseHandler,
                        requestContext);
            }
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public <R extends ExtendedResult> void handleExtendedRequest(final Integer messageID,
                final ExtendedRequest<R> request,
                final IntermediateResponseHandler intermediateResponseHandler,
                final ResultHandler<? super R> resultHandler) {
            if (request.getOID().equals(CancelExtendedRequest.OID)) {
                // Decode the request as a cancel request.
                CancelExtendedRequest cancelRequest;
                try {
                    cancelRequest =
                            CancelExtendedRequest.DECODER.decodeExtendedRequest(request,
                                    new DecodeOptions());
                } catch (final DecodeException e) {
                    // Couldn't decode a cancel request.
                    resultHandler.handleErrorResult(newErrorResult(ResultCode.PROTOCOL_ERROR, e
                            .getLocalizedMessage()));
                    return;
                }
                // Register the request in the pending requests table. Even
                // though
                // this request cannot be cancelled, it is important to do this
                // in
                // order to monitor the number of pending operations.
                final RequestContextImpl<R, ResultHandler<? super R>> requestContext =
                        new RequestContextImpl<R, ResultHandler<? super R>>(this, resultHandler,
                                messageID, false);
                if (addPendingRequest(requestContext)) {
                    // Find and cancel the request.
                    final RequestContextImpl<?, ?> cancelledRequest =
                            getPendingRequest(cancelRequest.getRequestID());
                    if (cancelledRequest != null) {
                        final LocalizableMessage cancelReason =
                                INFO_CANCELED_BY_CANCEL_REQUEST.get(messageID);
                        cancelledRequest.cancel(cancelReason, request, requestContext, true);
                    } else {
                        // Couldn't find the request. Invoke on context in order
                        // to remove
                        // pending request.
                        requestContext
                                .handleErrorResult(newErrorResult(ResultCode.NO_SUCH_OPERATION));
                    }
                }
            } else {
                final RequestContextImpl<R, ResultHandler<? super R>> requestContext;
                if (request.getOID().equals(StartTLSExtendedRequest.OID)) {
                    // StartTLS requests cannot be cancelled.
                    requestContext =
                            new RequestContextImpl<R, ResultHandler<? super R>>(this,
                                    resultHandler, messageID, false);
                } else {
                    requestContext =
                            new RequestContextImpl<R, ResultHandler<? super R>>(this,
                                    resultHandler, messageID, true);
                }
                if (addPendingRequest(requestContext)) {
                    requestHandler.handleExtendedRequest(requestContext, request,
                            intermediateResponseHandler, requestContext);
                }
            }
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleModify(final Integer messageID, final ModifyRequest request,
                final IntermediateResponseHandler intermediateResponseHandler,
                final ResultHandler<? super Result> resultHandler) {
            final RequestContextImpl<Result, ResultHandler<? super Result>> requestContext =
                    new RequestContextImpl<Result, ResultHandler<? super Result>>(this,
                            resultHandler, messageID, true);
            if (addPendingRequest(requestContext)) {
                requestHandler.handleModify(requestContext, request, intermediateResponseHandler,
                        requestContext);
            }
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleModifyDN(final Integer messageID, final ModifyDNRequest request,
                final IntermediateResponseHandler intermediateResponseHandler,
                final ResultHandler<? super Result> resultHandler) {
            final RequestContextImpl<Result, ResultHandler<? super Result>> requestContext =
                    new RequestContextImpl<Result, ResultHandler<? super Result>>(this,
                            resultHandler, messageID, true);
            if (addPendingRequest(requestContext)) {
                requestHandler.handleModifyDN(requestContext, request, intermediateResponseHandler,
                        requestContext);
            }
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleSearch(final Integer messageID, final SearchRequest request,
                final IntermediateResponseHandler intermediateResponseHandler,
                final SearchResultHandler resultHandler) {
            final SearchRequestContextImpl requestContext =
                    new SearchRequestContextImpl(this, resultHandler, messageID, true);
            if (addPendingRequest(requestContext)) {
                requestHandler.handleSearch(requestContext, request, intermediateResponseHandler,
                        requestContext);
            }
        }
        private boolean addPendingRequest(final RequestContextImpl<?, ?> requestContext) {
            final Integer messageID = requestContext.getMessageID();
            if (isClosed.get()) {
                final LocalizableMessage message = INFO_CLIENT_CONNECTION_CLOSING.get();
                requestContext.handleErrorResult(newErrorResult(ResultCode.UNWILLING_TO_PERFORM,
                        message.toString()));
                return false;
            } else if (pendingRequests.putIfAbsent(messageID, requestContext) != null) {
                final LocalizableMessage message =
                        WARN_CLIENT_DUPLICATE_MESSAGE_ID.get(requestContext.getMessageID());
                requestContext.handleErrorResult(newErrorResult(ResultCode.PROTOCOL_ERROR, message
                        .toString()));
                return false;
            } else if (isClosed.get()) {
                // A concurrent close may have already removed the pending
                // request but
                // it will have only been notified for cancellation.
                pendingRequests.remove(messageID);
                final LocalizableMessage message = INFO_CLIENT_CONNECTION_CLOSING.get();
                requestContext.handleErrorResult(newErrorResult(ResultCode.UNWILLING_TO_PERFORM,
                        message.toString()));
                return false;
            } else {
                // If the connection is closed now then we just have to pay the
                // cost of
                // invoking the request in the request handler.
                return true;
            }
        }
        private void doClose(final LocalizableMessage cancelReason) {
            if (!isClosed.getAndSet(true)) {
                // At this point if any pending requests are added then we may
                // end up
                // cancelling them, but this does not matter since
                // addPendingRequest
                // will fail the request immediately.
                final Iterator<RequestContextImpl<?, ?>> iterator =
                        pendingRequests.values().iterator();
                while (iterator.hasNext()) {
                    final RequestContextImpl<?, ?> pendingRequest = iterator.next();
                    pendingRequest.cancel(cancelReason, null, null, false);
                    iterator.remove();
                }
            }
        }
        /**
         * Returns the pending request context having the specified message ID.
         *
         * @param messageID
         *            The message ID associated with the request context.
         * @return The pending request context.
         */
        private RequestContextImpl<?, ?> getPendingRequest(final Integer messageID) {
            return pendingRequests.get(messageID);
        }
        /**
         * Deregister a request context once it has completed.
         *
         * @param requestContext
         *            The request context.
         * @return {@code true} if the request context was found and removed.
         */
        private boolean removePendingRequest(final RequestContextImpl<?, ?> requestContext) {
            return pendingRequests.remove(requestContext.getMessageID()) != null;
        }
    }
    private final RequestHandlerFactory<C, RequestContext> factory;
    /**
     * Sets the result associated with this request context and updates the
     * state accordingly.
     * Creates a new server connection factory using the provided request
     * handler factory.
     *
     * @param result
     *          The result.
     * @param factory
     *            The request handler factory to be adapted into a server
     *            connection factory.
     */
    private boolean setResult(final Result result)
    {
      List<ExtendedResultHandlerHolder<?>> tmpHandlers = null;
      boolean isCancelled = false;
      boolean maySendResult;
      synchronized (stateLock)
      {
        maySendResult = sendResult;
        switch (state)
        {
        case PENDING:
        case TOO_LATE:
          // Switch to appropriate final state.
          if (!result.getResultCode().equals(ResultCode.CANCELLED))
          {
            state = RequestState.RESULT_SENT;
          }
          else
          {
            state = RequestState.CANCELLED;
          }
          break;
        case CANCEL_REQUESTED:
          // Switch to appropriate final state and invoke any cancel request
          // handlers.
          if (!result.getResultCode().equals(ResultCode.CANCELLED))
          {
            state = RequestState.RESULT_SENT;
          }
          else
          {
            state = RequestState.CANCELLED;
          }
          isCancelled = (state == RequestState.CANCELLED);
          tmpHandlers = cancelResultHandlers;
          cancelResultHandlers = null;
          break;
        case RESULT_SENT:
        case CANCELLED:
          // This should not happen - could throw an illegal state exception?
          maySendResult = false; // Prevent sending multiple results.
          break;
        }
      }
      // Invoke handlers outside of lock.
      if (tmpHandlers != null)
      {
        for (final ExtendedResultHandlerHolder<?> handler : tmpHandlers)
        {
          if (isCancelled)
          {
            handler.handleSuccess();
          }
          else
          {
            handler.handleTooLate();
          }
        }
      }
      return maySendResult;
    RequestHandlerFactoryAdapter(final RequestHandlerFactory<C, RequestContext> factory) {
        this.factory = factory;
    }
  }
  /**
   * Search request context implementation.
   */
  private final static class SearchRequestContextImpl extends
      RequestContextImpl<Result, SearchResultHandler> implements
      SearchResultHandler
  {
    private SearchRequestContextImpl(
        final ServerConnectionImpl<?> clientConnection,
        final SearchResultHandler resultHandler, final int messageID,
        final boolean isCancelSupported)
    {
      super(clientConnection, resultHandler, messageID, isCancelSupported);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean handleEntry(final SearchResultEntry entry)
    {
      return resultHandler.handleEntry(entry);
    public ServerConnection<Integer> handleAccept(final C clientContext)
            throws ErrorResultException {
        final RequestHandler<RequestContext> requestHandler = factory.handleAccept(clientContext);
        return new ServerConnectionImpl<C>(requestHandler);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean handleReference(final SearchResultReference reference)
    {
      return resultHandler.handleReference(reference);
    }
  }
  private static final class ServerConnectionImpl<C> implements
      ServerConnection<Integer>
  {
    private final RequestHandler<RequestContext> requestHandler;
    private final AtomicBoolean isClosed = new AtomicBoolean();
    private final ConcurrentHashMap<Integer, RequestContextImpl<?, ?>> pendingRequests =
        new ConcurrentHashMap<Integer, RequestContextImpl<?, ?>>();
    private ServerConnectionImpl(
        final RequestHandler<RequestContext> requestHandler)
    {
      this.requestHandler = requestHandler;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleAbandon(final Integer messageID,
        final AbandonRequest request)
    {
      final RequestContextImpl<?, ?> abandonedRequest = getPendingRequest(request
          .getRequestID());
      if (abandonedRequest != null)
      {
        final LocalizableMessage abandonReason = INFO_CANCELED_BY_ABANDON_REQUEST
            .get(messageID);
        abandonedRequest.cancel(abandonReason, null, null, false);
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleAdd(final Integer messageID, final AddRequest request,
        final IntermediateResponseHandler intermediateResponseHandler,
        final ResultHandler<? super Result> resultHandler)
    {
      final RequestContextImpl<Result, ResultHandler<? super Result>> requestContext =
          new RequestContextImpl<Result, ResultHandler<? super Result>>(
          this, resultHandler, messageID, true);
      if (addPendingRequest(requestContext))
      {
        requestHandler.handleAdd(requestContext, request,
            intermediateResponseHandler, requestContext);
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleBind(final Integer messageID, final int version,
        final BindRequest request,
        final IntermediateResponseHandler intermediateResponseHandler,
        final ResultHandler<? super BindResult> resultHandler)
    {
      final RequestContextImpl<BindResult, ResultHandler<? super BindResult>> requestContext =
          new RequestContextImpl<BindResult, ResultHandler<? super BindResult>>(
          this, resultHandler, messageID, false);
      if (addPendingRequest(requestContext))
      {
        requestHandler.handleBind(requestContext, version, request,
            intermediateResponseHandler, requestContext);
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleCompare(final Integer messageID,
        final CompareRequest request,
        final IntermediateResponseHandler intermediateResponseHandler,
        final ResultHandler<? super CompareResult> resultHandler)
    {
      final RequestContextImpl<CompareResult, ResultHandler<? super CompareResult>> requestContext =
          new RequestContextImpl<CompareResult, ResultHandler<? super CompareResult>>(
          this, resultHandler, messageID, true);
      if (addPendingRequest(requestContext))
      {
        requestHandler.handleCompare(requestContext, request,
            intermediateResponseHandler, requestContext);
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleConnectionClosed(final Integer messageID,
        final UnbindRequest request)
    {
      final LocalizableMessage cancelReason = INFO_CANCELED_BY_CLIENT_DISCONNECT
          .get();
      doClose(cancelReason);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleConnectionDisconnected(final ResultCode resultCode,
        final String message)
    {
      final LocalizableMessage cancelReason = INFO_CANCELED_BY_SERVER_DISCONNECT
          .get();
      doClose(cancelReason);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleConnectionError(final Throwable error)
    {
      final LocalizableMessage cancelReason = INFO_CANCELED_BY_CLIENT_ERROR
          .get();
      doClose(cancelReason);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleDelete(final Integer messageID,
        final DeleteRequest request,
        final IntermediateResponseHandler intermediateResponseHandler,
        final ResultHandler<? super Result> resultHandler)
    {
      final RequestContextImpl<Result, ResultHandler<? super Result>> requestContext =
          new RequestContextImpl<Result, ResultHandler<? super Result>>(
          this, resultHandler, messageID, true);
      if (addPendingRequest(requestContext))
      {
        requestHandler.handleDelete(requestContext, request,
            intermediateResponseHandler, requestContext);
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public <R extends ExtendedResult> void handleExtendedRequest(
        final Integer messageID, final ExtendedRequest<R> request,
        final IntermediateResponseHandler intermediateResponseHandler,
        final ResultHandler<? super R> resultHandler)
    {
      if (request.getOID().equals(CancelExtendedRequest.OID))
      {
        // Decode the request as a cancel request.
        CancelExtendedRequest cancelRequest;
        try
        {
          cancelRequest = CancelExtendedRequest.DECODER.decodeExtendedRequest(
              request, new DecodeOptions());
        }
        catch (final DecodeException e)
        {
          // Couldn't decode a cancel request.
          resultHandler.handleErrorResult(newErrorResult(
              ResultCode.PROTOCOL_ERROR, e.getLocalizedMessage()));
          return;
        }
        // Register the request in the pending requests table. Even though
        // this request cannot be cancelled, it is important to do this in
        // order to monitor the number of pending operations.
        final RequestContextImpl<R, ResultHandler<? super R>> requestContext =
            new RequestContextImpl<R, ResultHandler<? super R>>(
            this, resultHandler, messageID, false);
        if (addPendingRequest(requestContext))
        {
          // Find and cancel the request.
          final RequestContextImpl<?, ?> cancelledRequest = getPendingRequest(cancelRequest
              .getRequestID());
          if (cancelledRequest != null)
          {
            final LocalizableMessage cancelReason = INFO_CANCELED_BY_CANCEL_REQUEST
                .get(messageID);
            cancelledRequest
                .cancel(cancelReason, request, requestContext, true);
          }
          else
          {
            // Couldn't find the request. Invoke on context in order to remove
            // pending request.
            requestContext
                .handleErrorResult(newErrorResult(ResultCode.NO_SUCH_OPERATION));
          }
        }
      }
      else
      {
        final RequestContextImpl<R, ResultHandler<? super R>> requestContext;
        if (request.getOID().equals(StartTLSExtendedRequest.OID))
        {
          // StartTLS requests cannot be cancelled.
          requestContext = new RequestContextImpl<R, ResultHandler<? super R>>(
              this, resultHandler, messageID, false);
        }
        else
        {
          requestContext = new RequestContextImpl<R, ResultHandler<? super R>>(
              this, resultHandler, messageID, true);
        }
        if (addPendingRequest(requestContext))
        {
          requestHandler.handleExtendedRequest(requestContext, request,
              intermediateResponseHandler, requestContext);
        }
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleModify(final Integer messageID,
        final ModifyRequest request,
        final IntermediateResponseHandler intermediateResponseHandler,
        final ResultHandler<? super Result> resultHandler)
    {
      final RequestContextImpl<Result, ResultHandler<? super Result>> requestContext =
          new RequestContextImpl<Result, ResultHandler<? super Result>>(
          this, resultHandler, messageID, true);
      if (addPendingRequest(requestContext))
      {
        requestHandler.handleModify(requestContext, request,
            intermediateResponseHandler, requestContext);
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleModifyDN(final Integer messageID,
        final ModifyDNRequest request,
        final IntermediateResponseHandler intermediateResponseHandler,
        final ResultHandler<? super Result> resultHandler)
    {
      final RequestContextImpl<Result, ResultHandler<? super Result>> requestContext =
          new RequestContextImpl<Result, ResultHandler<? super Result>>(
          this, resultHandler, messageID, true);
      if (addPendingRequest(requestContext))
      {
        requestHandler.handleModifyDN(requestContext, request,
            intermediateResponseHandler, requestContext);
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleSearch(final Integer messageID,
        final SearchRequest request,
        final IntermediateResponseHandler intermediateResponseHandler,
        final SearchResultHandler resultHandler)
    {
      final SearchRequestContextImpl requestContext = new SearchRequestContextImpl(
          this, resultHandler, messageID, true);
      if (addPendingRequest(requestContext))
      {
        requestHandler.handleSearch(requestContext, request,
            intermediateResponseHandler, requestContext);
      }
    }
    private boolean addPendingRequest(
        final RequestContextImpl<?, ?> requestContext)
    {
      final Integer messageID = requestContext.getMessageID();
      if (isClosed.get())
      {
        final LocalizableMessage message = INFO_CLIENT_CONNECTION_CLOSING.get();
        requestContext.handleErrorResult(newErrorResult(
            ResultCode.UNWILLING_TO_PERFORM, message.toString()));
        return false;
      }
      else if (pendingRequests.putIfAbsent(messageID, requestContext) != null)
      {
        final LocalizableMessage message = WARN_CLIENT_DUPLICATE_MESSAGE_ID
            .get(requestContext.getMessageID());
        requestContext.handleErrorResult(newErrorResult(
            ResultCode.PROTOCOL_ERROR, message.toString()));
        return false;
      }
      else if (isClosed.get())
      {
        // A concurrent close may have already removed the pending request but
        // it will have only been notified for cancellation.
        pendingRequests.remove(messageID);
        final LocalizableMessage message = INFO_CLIENT_CONNECTION_CLOSING.get();
        requestContext.handleErrorResult(newErrorResult(
            ResultCode.UNWILLING_TO_PERFORM, message.toString()));
        return false;
      }
      else
      {
        // If the connection is closed now then we just have to pay the cost of
        // invoking the request in the request handler.
        return true;
      }
    }
    private void doClose(final LocalizableMessage cancelReason)
    {
      if (!isClosed.getAndSet(true))
      {
        // At this point if any pending requests are added then we may end up
        // cancelling them, but this does not matter since addPendingRequest
        // will fail the request immediately.
        final Iterator<RequestContextImpl<?, ?>> iterator = pendingRequests
            .values().iterator();
        while (iterator.hasNext())
        {
          final RequestContextImpl<?, ?> pendingRequest = iterator.next();
          pendingRequest.cancel(cancelReason, null, null, false);
          iterator.remove();
        }
      }
    }
    /**
     * Returns the pending request context having the specified message ID.
     *
     * @param messageID
     *          The message ID associated with the request context.
     * @return The pending request context.
     */
    private RequestContextImpl<?, ?> getPendingRequest(final Integer messageID)
    {
      return pendingRequests.get(messageID);
    }
    /**
     * Deregister a request context once it has completed.
     *
     * @param requestContext
     *          The request context.
     * @return {@code true} if the request context was found and removed.
     */
    private boolean removePendingRequest(
        final RequestContextImpl<?, ?> requestContext)
    {
      return pendingRequests.remove(requestContext.getMessageID()) != null;
    }
  }
  private final RequestHandlerFactory<C, RequestContext> factory;
  /**
   * Creates a new server connection factory using the provided request handler
   * factory.
   *
   * @param factory
   *          The request handler factory to be adapted into a server connection
   *          factory.
   */
  RequestHandlerFactoryAdapter(
      final RequestHandlerFactory<C, RequestContext> factory)
  {
    this.factory = factory;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public ServerConnection<Integer> handleAccept(final C clientContext)
      throws ErrorResultException
  {
    final RequestHandler<RequestContext> requestHandler = factory
        .handleAccept(clientContext);
    return new ServerConnectionImpl<C>(requestHandler);
  }
}