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}