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