| | |
| | | * (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 |
| | |
| | | |
| | | package org.forgerock.opendj.ldap; |
| | | |
| | | |
| | | |
| | | import static org.forgerock.opendj.ldap.CoreMessages.*; |
| | | import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult; |
| | | |
| | |
| | | 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); |
| | | } |
| | | |
| | | } |