001/* 002 * Copyright 2007-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2007-2024 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2007-2024 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.ldap.sdk; 037 038 039 040import java.util.Arrays; 041import java.util.Collections; 042import java.util.List; 043 044import com.unboundid.util.Extensible; 045import com.unboundid.util.InternalUseOnly; 046import com.unboundid.util.NotNull; 047import com.unboundid.util.Nullable; 048import com.unboundid.util.ThreadSafety; 049import com.unboundid.util.ThreadSafetyLevel; 050import com.unboundid.util.Validator; 051 052 053 054/** 055 * This class provides a framework that should be extended by all types of LDAP 056 * requests. It provides methods for interacting with the set of controls to 057 * include as part of the request and configuring a response timeout, which is 058 * the maximum length of time that the SDK should wait for a response to the 059 * request before returning an error back to the caller. 060 * <BR><BR> 061 * {@code LDAPRequest} objects are not immutable and should not be considered 062 * threadsafe. A single {@code LDAPRequest} object instance should not be used 063 * concurrently by multiple threads, but instead each thread wishing to process 064 * a request should have its own instance of that request. The 065 * {@link #duplicate()} method may be used to create an exact copy of a request 066 * suitable for processing by a separate thread. 067 * <BR><BR> 068 * Note that even though this class is marked with the @Extensible annotation 069 * type, it should not be directly subclassed by third-party code. Only the 070 * {@link ExtendedRequest} and {@link SASLBindRequest} subclasses are actually 071 * intended to be extended by third-party code. 072 */ 073@Extensible() 074@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 075public abstract class LDAPRequest 076 implements ReadOnlyLDAPRequest 077{ 078 /** 079 * The set of controls that will be used if none were provided. 080 */ 081 @NotNull static final Control[] NO_CONTROLS = new Control[0]; 082 083 084 085 /** 086 * The serial version UID for this serializable class. 087 */ 088 private static final long serialVersionUID = -2040756188243320117L; 089 090 091 092 // Indicates whether to automatically follow referrals returned while 093 // processing this request. 094 @Nullable private Boolean followReferrals; 095 096 // The set of controls for this request. 097 @NotNull private Control[] controls; 098 099 // The current depth to use when following referrals. 100 private int referralDepth; 101 102 // The intermediate response listener for this request. 103 @Nullable private IntermediateResponseListener intermediateResponseListener; 104 105 // The maximum length of time in milliseconds to wait for the response from 106 // the server. The default value of -1 indicates that it should be inherited 107 // from the associated connection. 108 private long responseTimeout; 109 110 // The referral connector to use when following referrals. 111 @Nullable private ReferralConnector referralConnector; 112 113 114 115 /** 116 * Creates a new LDAP request with the provided set of controls. 117 * 118 * @param controls The set of controls to include in this LDAP request. 119 */ 120 protected LDAPRequest(@Nullable final Control[] controls) 121 { 122 if (controls == null) 123 { 124 this.controls = NO_CONTROLS; 125 } 126 else 127 { 128 this.controls = controls; 129 } 130 131 followReferrals = null; 132 referralDepth = 1; 133 responseTimeout = -1L; 134 intermediateResponseListener = null; 135 referralConnector = null; 136 } 137 138 139 140 /** 141 * {@inheritDoc} 142 */ 143 @Override() 144 @NotNull() 145 public final Control[] getControls() 146 { 147 return controls; 148 } 149 150 151 152 /** 153 * {@inheritDoc} 154 */ 155 @Override() 156 @NotNull() 157 public final List<Control> getControlList() 158 { 159 return Collections.unmodifiableList(Arrays.asList(controls)); 160 } 161 162 163 164 /** 165 * {@inheritDoc} 166 */ 167 @Override() 168 public final boolean hasControl() 169 { 170 return (controls.length > 0); 171 } 172 173 174 175 /** 176 * {@inheritDoc} 177 */ 178 @Override() 179 public final boolean hasControl(@NotNull final String oid) 180 { 181 Validator.ensureNotNull(oid); 182 183 for (final Control c : controls) 184 { 185 if (c.getOID().equals(oid)) 186 { 187 return true; 188 } 189 } 190 191 return false; 192 } 193 194 195 196 /** 197 * {@inheritDoc} 198 */ 199 @Override() 200 @Nullable() 201 public final Control getControl(@NotNull final String oid) 202 { 203 Validator.ensureNotNull(oid); 204 205 for (final Control c : controls) 206 { 207 if (c.getOID().equals(oid)) 208 { 209 return c; 210 } 211 } 212 213 return null; 214 } 215 216 217 218 /** 219 * Updates the set of controls associated with this request. This must only 220 * be called by {@link UpdatableLDAPRequest}. 221 * 222 * @param controls The set of controls to use for this request. 223 */ 224 final void setControlsInternal(@NotNull final Control[] controls) 225 { 226 this.controls = controls; 227 } 228 229 230 231 /** 232 * {@inheritDoc} 233 */ 234 @Override() 235 public final long getResponseTimeoutMillis( 236 @Nullable final LDAPConnection connection) 237 { 238 if ((responseTimeout < 0L) && (connection != null)) 239 { 240 if (this instanceof ExtendedRequest) 241 { 242 final ExtendedRequest extendedRequest = (ExtendedRequest) this; 243 return connection.getConnectionOptions(). 244 getExtendedOperationResponseTimeoutMillis( 245 extendedRequest.getOID()); 246 } 247 else 248 { 249 return connection.getConnectionOptions().getResponseTimeoutMillis( 250 getOperationType()); 251 } 252 } 253 else 254 { 255 return responseTimeout; 256 } 257 } 258 259 260 261 /** 262 * Specifies the maximum length of time in milliseconds that processing on 263 * this operation should be allowed to block while waiting for a response 264 * from the server. A value of zero indicates that no timeout should be 265 * enforced. A value that is less than zero indicates that the default 266 * response timeout for the underlying connection should be used. 267 * 268 * @param responseTimeout The maximum length of time in milliseconds that 269 * processing on this operation should be allowed to 270 * block while waiting for a response from the 271 * server. 272 */ 273 public final void setResponseTimeoutMillis(final long responseTimeout) 274 { 275 if (responseTimeout < 0L) 276 { 277 this.responseTimeout = -1L; 278 } 279 else 280 { 281 this.responseTimeout = responseTimeout; 282 } 283 } 284 285 286 287 /** 288 * Indicates whether to automatically follow any referrals encountered while 289 * processing this request. If a value has been set for this request, then it 290 * will be returned. Otherwise, the default from the connection options for 291 * the provided connection will be used. 292 * 293 * @param connection The connection whose connection options may be used in 294 * the course of making the determination. It must not 295 * be {@code null}. 296 * 297 * @return {@code true} if any referrals encountered during processing should 298 * be automatically followed, or {@code false} if not. 299 */ 300 @Override() 301 public final boolean followReferrals(@NotNull final LDAPConnection connection) 302 { 303 if (followReferrals == null) 304 { 305 return connection.getConnectionOptions().followReferrals(); 306 } 307 else 308 { 309 return followReferrals; 310 } 311 } 312 313 314 315 /** 316 * Indicates whether automatic referral following is enabled for this request. 317 * 318 * @return {@code Boolean.TRUE} if automatic referral following is enabled 319 * for this request, {@code Boolean.FALSE} if not, or {@code null} if 320 * a per-request behavior is not specified. 321 */ 322 @Nullable() 323 final Boolean followReferralsInternal() 324 { 325 return followReferrals; 326 } 327 328 329 330 /** 331 * Specifies whether to automatically follow any referrals encountered while 332 * processing this request. This may be used to override the default behavior 333 * defined in the connection options for the connection used to process the 334 * request. 335 * 336 * @param followReferrals Indicates whether to automatically follow any 337 * referrals encountered while processing this 338 * request. It may be {@code null} to indicate that 339 * the determination should be based on the 340 * connection options for the connection used to 341 * process the request. 342 */ 343 public final void setFollowReferrals(@Nullable final Boolean followReferrals) 344 { 345 this.followReferrals = followReferrals; 346 } 347 348 349 350 /** 351 * Retrieves the current depth to use when following referrals. 352 * 353 * @return The current depth to use when following referrals. 354 */ 355 protected int getReferralDepth() 356 { 357 return referralDepth; 358 } 359 360 361 362 /** 363 * Sets the current depth to use when following referrals. 364 * 365 * @param referralDepth The current depth to use when following referrals. 366 */ 367 protected void setReferralDepth(final int referralDepth) 368 { 369 this.referralDepth = referralDepth; 370 } 371 372 373 374 /** 375 * {@inheritDoc} 376 */ 377 @Override() 378 @NotNull() 379 public final ReferralConnector getReferralConnector( 380 @NotNull final LDAPConnection connection) 381 { 382 if (referralConnector == null) 383 { 384 return connection.getReferralConnector(); 385 } 386 else 387 { 388 return referralConnector; 389 } 390 } 391 392 393 394 /** 395 * Retrieves the referral connector that has been set for this request. 396 * 397 * @return The referral connector that has been set for this request, or 398 * {@code null} if no referral connector has been set for this 399 * request and the connection's default referral connector will be 400 * used if necessary. 401 */ 402 @Nullable() 403 protected final ReferralConnector getReferralConnectorInternal() 404 { 405 return referralConnector; 406 } 407 408 409 410 /** 411 * Sets the referral connector that should be used to establish connections 412 * for the purpose of following any referrals encountered when processing this 413 * request. 414 * 415 * @param referralConnector The referral connector that should be used to 416 * establish connections for the purpose of 417 * following any referral encountered when 418 * processing this request. It may be 419 * {@code null} to use the default referral handler 420 * for the connection on which the referral was 421 * received. 422 */ 423 public final void setReferralConnector( 424 @Nullable final ReferralConnector referralConnector) 425 { 426 this.referralConnector = referralConnector; 427 } 428 429 430 431 /** 432 * Retrieves the intermediate response listener for this request, if any. 433 * 434 * @return The intermediate response listener for this request, or 435 * {@code null} if there is none. 436 */ 437 @Nullable() 438 public final IntermediateResponseListener getIntermediateResponseListener() 439 { 440 return intermediateResponseListener; 441 } 442 443 444 445 /** 446 * Sets the intermediate response listener for this request. 447 * 448 * @param listener The intermediate response listener for this request. It 449 * may be {@code null} to clear any existing listener. 450 */ 451 public final void setIntermediateResponseListener( 452 @Nullable final IntermediateResponseListener listener) 453 { 454 intermediateResponseListener = listener; 455 } 456 457 458 459 /** 460 * Processes this operation using the provided connection and returns the 461 * result. 462 * 463 * @param connection The connection to use to process the request. 464 * @param depth The current referral depth for this request. It should 465 * always be one for the initial request, and should only 466 * be incremented when following referrals. 467 * 468 * @return The result of processing this operation. 469 * 470 * @throws LDAPException If a problem occurs while processing the request. 471 */ 472 @InternalUseOnly() 473 @NotNull() 474 protected abstract LDAPResult process(@NotNull LDAPConnection connection, 475 int depth) 476 throws LDAPException; 477 478 479 480 /** 481 * Retrieves the message ID for the last LDAP message sent using this request. 482 * 483 * @return The message ID for the last LDAP message sent using this request, 484 * or -1 if it no LDAP messages have yet been sent using this 485 * request. 486 */ 487 public abstract int getLastMessageID(); 488 489 490 491 /** 492 * Retrieves the type of operation that is represented by this request. 493 * 494 * @return The type of operation that is represented by this request. 495 */ 496 @NotNull() 497 public abstract OperationType getOperationType(); 498 499 500 501 /** 502 * {@inheritDoc} 503 */ 504 @Override() 505 @NotNull() 506 public String toString() 507 { 508 final StringBuilder buffer = new StringBuilder(); 509 toString(buffer); 510 return buffer.toString(); 511 } 512 513 514 515 /** 516 * {@inheritDoc} 517 */ 518 @Override() 519 public abstract void toString(@NotNull StringBuilder buffer); 520}