001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2014-2015 ForgeRock AS.
025 */
026package org.forgerock.opendj.ldap.spi;
027
028import static org.forgerock.opendj.ldap.LdapException.newLdapException;
029import static org.forgerock.opendj.ldap.responses.Responses.newCompareResult;
030import static org.forgerock.opendj.ldap.responses.Responses.newResult;
031
032import org.forgerock.opendj.ldap.Connection;
033import org.forgerock.opendj.ldap.IntermediateResponseHandler;
034import org.forgerock.opendj.ldap.LdapException;
035import org.forgerock.opendj.ldap.LdapPromise;
036import org.forgerock.opendj.ldap.ResultCode;
037import org.forgerock.opendj.ldap.SearchResultHandler;
038import org.forgerock.opendj.ldap.requests.BindClient;
039import org.forgerock.opendj.ldap.requests.BindRequest;
040import org.forgerock.opendj.ldap.requests.CompareRequest;
041import org.forgerock.opendj.ldap.requests.ExtendedRequest;
042import org.forgerock.opendj.ldap.requests.Request;
043import org.forgerock.opendj.ldap.requests.Requests;
044import org.forgerock.opendj.ldap.requests.SearchRequest;
045import org.forgerock.opendj.ldap.requests.StartTLSExtendedRequest;
046import org.forgerock.opendj.ldap.responses.BindResult;
047import org.forgerock.opendj.ldap.responses.CompareResult;
048import org.forgerock.opendj.ldap.responses.ExtendedResult;
049import org.forgerock.opendj.ldap.responses.Result;
050import org.forgerock.util.promise.Promise;
051import org.forgerock.util.promise.PromiseImpl;
052import org.forgerock.util.promise.Promises;
053
054/**
055 * Utility methods for creating and composing {@link LdapPromise}s.
056 */
057public final class LdapPromises {
058    private LdapPromises() {
059    }
060
061    /**
062     * Converts a {@link Promise} to a {@link LdapPromise}.
063     *
064     * @param <R>
065     *         The type of the task's result, or {@link Void} if the task does not return anything (i.e. it only has
066     *         side-effects).
067     * @param wrappedPromise
068     *         The {@link Promise} to wrap.
069     * @return A {@link LdapPromise} representing the same asynchronous task as the {@link Promise} provided.
070     */
071    public static <R> LdapPromise<R> asPromise(Promise<R, LdapException> wrappedPromise) {
072        return wrap(wrappedPromise, -1);
073    }
074
075    /**
076     * Creates a new bind {@link BindResultLdapPromiseImpl}.
077     *
078     * @param requestID
079     *         Identifier of the request.
080     * @param request
081     *         The bind request sent to server.
082     * @param bindClient
083     *         Client that binds to the server.
084     * @param intermediateResponseHandler
085     *         Handler that consumes intermediate responses from extended operations.
086     * @param connection
087     *         The connection to directory server.
088     * @return The new {@link BindResultLdapPromiseImpl}.
089     */
090    public static BindResultLdapPromiseImpl newBindLdapPromise(
091            final int requestID,
092            final BindRequest request,
093            final BindClient bindClient,
094            final IntermediateResponseHandler intermediateResponseHandler,
095            final Connection connection) {
096        return new BindResultLdapPromiseImpl(LdapPromises.<BindResult>newInnerBindOrStartTLSPromise(),
097                                             requestID,
098                                             request,
099                                             bindClient,
100                                             intermediateResponseHandler);
101    }
102
103    /**
104     * Creates a new bind {@link BindResultLdapPromiseImpl}.
105     *
106     * @param requestID
107     *         Identifier of the request.
108     * @param request
109     *         The bind request sent to server.
110     * @param bindClient
111     *         Client that binds to the server.
112     * @param intermediateResponseHandler
113     *         Handler that consumes intermediate responses from extended operations.
114     * @param connection
115     *         The connection to directory server.
116     * @return The new {@link BindResultLdapPromiseImpl}.
117     */
118    public static BindResultLdapPromiseImpl newBindLdapPromise(
119            final int requestID,
120            final BindRequest request,
121            final BindClient bindClient,
122            final IntermediateResponseHandler intermediateResponseHandler,
123            final LDAPConnectionImpl connection) {
124        return new BindResultLdapPromiseImpl(LdapPromises.<BindResult>newInnerBindOrStartTLSPromise(),
125                                             requestID,
126                                             request,
127                                             bindClient,
128                                             intermediateResponseHandler);
129    }
130
131    /**
132     * Creates a new compare {@link ResultLdapPromiseImpl}.
133     *
134     * @param requestID
135     *         Identifier of the request.
136     * @param request
137     *         The compare request sent to the server.
138     * @param intermediateResponseHandler
139     *         Handler that consumes intermediate responses from extended operations.
140     * @param connection
141     *         The connection to directory server.
142     * @return The new {@link ResultLdapPromiseImpl}.
143     */
144    public static ResultLdapPromiseImpl<CompareRequest, CompareResult> newCompareLdapPromise(
145            final int requestID,
146            final CompareRequest request,
147            final IntermediateResponseHandler intermediateResponseHandler,
148            final Connection connection) {
149        final PromiseImpl<CompareResult, LdapException> innerPromise = newInnerPromise(connection, requestID);
150        return newCompareLdapPromise(innerPromise, requestID, request, intermediateResponseHandler);
151    }
152
153    /**
154     * Creates a new compare {@link ResultLdapPromiseImpl}.
155     *
156     * @param requestID
157     *         Identifier of the request.
158     * @param request
159     *         The compare request sent to the server.
160     * @param intermediateResponseHandler
161     *         Handler that consumes intermediate responses from extended operations.
162     * @param connection
163     *         The connection to directory server.
164     * @return The new {@link ResultLdapPromiseImpl}.
165     */
166    public static ResultLdapPromiseImpl<CompareRequest, CompareResult> newCompareLdapPromise(
167            final int requestID,
168            final CompareRequest request,
169            final IntermediateResponseHandler intermediateResponseHandler,
170            final LDAPConnectionImpl connection) {
171        final PromiseImpl<CompareResult, LdapException> innerPromise = newInnerPromise(connection, requestID);
172        return newCompareLdapPromise(innerPromise, requestID, request, intermediateResponseHandler);
173    }
174
175    private static ResultLdapPromiseImpl<CompareRequest, CompareResult> newCompareLdapPromise(
176            final PromiseImpl<CompareResult, LdapException> innerPromise,
177            final int requestID,
178            final CompareRequest request,
179            final IntermediateResponseHandler intermediateResponseHandler) {
180        return new ResultLdapPromiseImpl<CompareRequest, CompareResult>(innerPromise,
181                                                                        requestID,
182                                                                        request,
183                                                                        intermediateResponseHandler) {
184            @Override
185            protected CompareResult newErrorResult(ResultCode resultCode, String diagnosticMessage, Throwable cause) {
186                return newCompareResult(resultCode).setDiagnosticMessage(diagnosticMessage).setCause(cause);
187            }
188        };
189    }
190
191    /**
192     * Creates a new extended {@link ExtendedResultLdapPromiseImpl}.
193     *
194     * @param <S>
195     *         The type of result returned by this promise.
196     * @param requestID
197     *         Identifier of the request.
198     * @param request
199     *         The extended request sent to the server.
200     * @param intermediateResponseHandler
201     *         Handler that consumes intermediate responses from extended operations.
202     * @param connection
203     *         The connection to directory server.
204     * @return The new {@link ExtendedResultLdapPromiseImpl}.
205     */
206    public static <S extends ExtendedResult> ExtendedResultLdapPromiseImpl<S> newExtendedLdapPromise(
207            final int requestID,
208            final ExtendedRequest<S> request,
209            final IntermediateResponseHandler intermediateResponseHandler,
210            final Connection connection) {
211        final PromiseImpl<S, LdapException> innerPromise;
212        if (!StartTLSExtendedRequest.OID.equals(request.getOID())) {
213            innerPromise = newInnerBindOrStartTLSPromise();
214        } else {
215            innerPromise = newInnerPromise(connection, requestID);
216        }
217        return new ExtendedResultLdapPromiseImpl<>(innerPromise, requestID, request, intermediateResponseHandler);
218    }
219
220    /**
221     * Creates a new extended {@link ExtendedResultLdapPromiseImpl}.
222     *
223     * @param <S>
224     *         The type of result returned by this promise.
225     * @param requestID
226     *         Identifier of the request.
227     * @param request
228     *         The extended request sent to the server.
229     * @param intermediateResponseHandler
230     *         Handler that consumes intermediate responses from extended operations.
231     * @param connection
232     *         The connection to directory server.
233     * @return The new {@link ExtendedResultLdapPromiseImpl}.
234     */
235    public static <S extends ExtendedResult> ExtendedResultLdapPromiseImpl<S> newExtendedLdapPromise(
236            final int requestID,
237            final ExtendedRequest<S> request,
238            final IntermediateResponseHandler intermediateResponseHandler,
239            final LDAPConnectionImpl connection) {
240        final PromiseImpl<S, LdapException> innerPromise;
241        if (!StartTLSExtendedRequest.OID.equals(request.getOID())) {
242            innerPromise = newInnerBindOrStartTLSPromise();
243        } else {
244            innerPromise = newInnerPromise(connection, requestID);
245        }
246        return new ExtendedResultLdapPromiseImpl<>(innerPromise, requestID, request, intermediateResponseHandler);
247    }
248
249    /**
250     * Creates a new {@link ResultLdapPromiseImpl} to handle  a standard request (add, delete, modify and modidyDN).
251     *
252     * @param <R>
253     *         The type of the task's request.
254     * @param requestID
255     *         Identifier of the request.
256     * @param request
257     *         The request sent to the server.
258     * @param intermediateResponseHandler
259     *         Handler that consumes intermediate responses from extended operations.
260     * @param connection
261     *         The connection to directory server.
262     * @return The new {@link ResultLdapPromiseImpl}.
263     */
264    public static <R extends Request> ResultLdapPromiseImpl<R, Result> newResultLdapPromise(
265            final int requestID,
266            final R request,
267            final IntermediateResponseHandler intermediateResponseHandler,
268            final Connection connection) {
269        final PromiseImpl<Result, LdapException> innerPromise = newInnerPromise(connection, requestID);
270        return newResultLdapPromise(innerPromise, requestID, request, intermediateResponseHandler);
271    }
272
273    /**
274     * Creates a new {@link ResultLdapPromiseImpl} to handle  a standard request (add, delete, modify and modidyDN).
275     *
276     * @param <R>
277     *         The type of the task's request.
278     * @param requestID
279     *         Identifier of the request.
280     * @param request
281     *         The request sent to the server.
282     * @param intermediateResponseHandler
283     *         Handler that consumes intermediate responses from extended operations.
284     * @param connection
285     *         The connection to directory server.
286     * @return The new {@link ResultLdapPromiseImpl}.
287     */
288    public static <R extends Request> ResultLdapPromiseImpl<R, Result> newResultLdapPromise(
289            final int requestID,
290            final R request,
291            final IntermediateResponseHandler intermediateResponseHandler,
292            final LDAPConnectionImpl connection) {
293        final PromiseImpl<Result, LdapException> innerPromise = newInnerPromise(connection, requestID);
294        return newResultLdapPromise(innerPromise, requestID, request, intermediateResponseHandler);
295    }
296
297    private static <R extends Request> ResultLdapPromiseImpl<R, Result> newResultLdapPromise(
298            final PromiseImpl<Result, LdapException> innerPromise,
299            final int requestID,
300            final R request,
301            final IntermediateResponseHandler intermediateResponseHandler) {
302        return new ResultLdapPromiseImpl<R, Result>(innerPromise, requestID, request, intermediateResponseHandler) {
303            @Override
304            protected Result newErrorResult(ResultCode resultCode, String diagnosticMessage, Throwable cause) {
305                return newResult(resultCode).setDiagnosticMessage(diagnosticMessage).setCause(cause);
306            }
307        };
308    }
309
310    /**
311     * Creates a new search {@link SearchResultLdapPromiseImpl}.
312     *
313     * @param requestID
314     *         Identifier of the request.
315     * @param request
316     *         The search request sent to the server.
317     * @param resultHandler
318     *         Handler that consumes search result.
319     * @param intermediateResponseHandler
320     *         Handler that consumes intermediate responses from extended operations.
321     * @param connection
322     *         The connection to directory server.
323     * @return The new {@link SearchResultLdapPromiseImpl}.
324     */
325    public static SearchResultLdapPromiseImpl newSearchLdapPromise(
326            final int requestID,
327            final SearchRequest request,
328            final SearchResultHandler resultHandler,
329            final IntermediateResponseHandler intermediateResponseHandler,
330            final Connection connection) {
331        return new SearchResultLdapPromiseImpl(newInnerPromise(connection, requestID),
332                                               requestID,
333                                               request,
334                                               resultHandler,
335                                               intermediateResponseHandler);
336    }
337
338    /**
339     * Creates a new search {@link SearchResultLdapPromiseImpl}.
340     *
341     * @param requestID
342     *         Identifier of the request.
343     * @param request
344     *         The search request sent to the server.
345     * @param resultHandler
346     *         Handler that consumes search result.
347     * @param intermediateResponseHandler
348     *         Handler that consumes intermediate responses from extended operations.
349     * @param connection
350     *         The connection to directory server.
351     * @return The new {@link SearchResultLdapPromiseImpl}.
352     */
353    public static SearchResultLdapPromiseImpl newSearchLdapPromise(
354            final int requestID,
355            final SearchRequest request,
356            final SearchResultHandler resultHandler,
357            final IntermediateResponseHandler intermediateResponseHandler,
358            final LDAPConnectionImpl connection) {
359        return new SearchResultLdapPromiseImpl(newInnerPromise(connection, requestID),
360                                               requestID,
361                                               request,
362                                               resultHandler,
363                                               intermediateResponseHandler);
364    }
365
366    /**
367     * Returns a {@link LdapPromise} representing an asynchronous task which has already failed with the provided
368     * error.
369     *
370     * @param <R>
371     *         The type of the task's result, or {@link Void} if the task does not return anything (i.e. it only has
372     *         side-effects).
373     * @param <E>
374     *         The type of the exception thrown by the task if it fails.
375     * @param error
376     *         The exception indicating why the asynchronous task has failed.
377     * @return A {@link LdapPromise} representing an asynchronous task which has already failed with the provided error.
378     */
379    public static <R, E extends LdapException> LdapPromise<R> newFailedLdapPromise(final E error) {
380        return wrap(Promises.<R, LdapException>newExceptionPromise(error), -1);
381    }
382
383    /**
384     * Returns a {@link LdapPromise} representing an asynchronous task, identified by the provided requestID, which has
385     * already failed with the provided error.
386     *
387     * @param <R>
388     *         The type of the task's result, or {@link Void} if the task does not return anything (i.e. it only has
389     *         side-effects).
390     * @param <E>
391     *         The type of the exception thrown by the task if it fails.
392     * @param error
393     *         The exception indicating why the asynchronous task has failed.
394     * @param requestID
395     *         The request ID of the failed task.
396     * @return A {@link LdapPromise} representing an asynchronous task which has already failed with the provided error.
397     */
398    public static <R, E extends LdapException> LdapPromise<R> newFailedLdapPromise(final E error, int requestID) {
399        return wrap(Promises.<R, LdapException>newExceptionPromise(error), requestID);
400    }
401
402    /**
403     * Returns a {@link LdapPromise} representing an asynchronous task which has already succeeded with the provided
404     * result. Attempts to get the result will immediately return the result.
405     *
406     * @param <R>
407     *         The type of the task's result, or {@link Void} if the task does not return anything (i.e. it only has
408     *         side-effects).
409     * @param result
410     *         The result of the asynchronous task.
411     * @return A {@link LdapPromise} representing an asynchronous task which has already succeeded with the provided
412     * result.
413     */
414    public static <R> LdapPromise<R> newSuccessfulLdapPromise(final R result) {
415        return wrap(Promises.<R, LdapException>newResultPromise(result), -1);
416    }
417
418    /**
419     * Returns a {@link LdapPromise} representing an asynchronous task, identified by the provided requestID, which has
420     * already succeeded with the provided result. Attempts to get the result will immediately return the result.
421     *
422     * @param <R>
423     *         The type of the task's result, or {@link Void} if the task does not return anything (i.e. it only has
424     *         side-effects).
425     * @param result
426     *         The result of the asynchronous task.
427     * @param requestID
428     *         The request ID of the succeeded task.
429     * @return A {@link LdapPromise} representing an asynchronous task which has already succeeded with the provided
430     * result.
431     */
432    public static <R> LdapPromise<R> newSuccessfulLdapPromise(final R result, int requestID) {
433        return wrap(Promises.<R, LdapException>newResultPromise(result), requestID);
434    }
435
436    private static <S extends Result> PromiseImpl<S, LdapException> newInnerBindOrStartTLSPromise() {
437        return new PromiseImpl<S, LdapException>() {
438            @Override
439            protected LdapException tryCancel(boolean mayInterruptIfRunning) {
440                /*
441                 * No other operations can be performed while a bind or StartTLS is active. Therefore it is not
442                 * possible to cancel bind or StartTLS requests, since doing so will leave the connection in a
443                 * state which prevents other operations from being performed.
444                 */
445                return null;
446            }
447        };
448    }
449
450    private static <S extends Result> PromiseImpl<S, LdapException> newInnerPromise(
451            final Connection connection, final int requestID) {
452        return new PromiseImpl<S, LdapException>() {
453            @Override
454            protected final LdapException tryCancel(final boolean mayInterruptIfRunning) {
455                /*
456                 * This will abandon the request, but will also recursively cancel this future. There is no risk of
457                 * an infinite loop because the state of this future has already been changed.
458                 */
459                connection.abandonAsync(Requests.newAbandonRequest(requestID));
460                return newLdapException(ResultCode.CLIENT_SIDE_USER_CANCELLED);
461            }
462        };
463    }
464
465    private static <S extends Result> PromiseImpl<S, LdapException> newInnerPromise(
466            final LDAPConnectionImpl connection, final int requestID) {
467        return new PromiseImpl<S, LdapException>() {
468            @Override
469            protected final LdapException tryCancel(final boolean mayInterruptIfRunning) {
470                /*
471                 * This will abandon the request, but will also recursively cancel this future. There is no risk of
472                 * an infinite loop because the state of this future has already been changed.
473                 */
474                connection.abandonAsync(Requests.newAbandonRequest(requestID));
475                return newLdapException(ResultCode.CLIENT_SIDE_USER_CANCELLED);
476            }
477        };
478    }
479
480    static <R> LdapPromise<R> wrap(Promise<R, LdapException> wrappedPromise, int requestID) {
481        return new LdapPromiseWrapper<>(wrappedPromise, requestID);
482    }
483}