001/* 002 * Copyright 2007-2022 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2007-2022 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-2022 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 intermediate response listener for this request. 100 @Nullable private IntermediateResponseListener intermediateResponseListener; 101 102 // The maximum length of time in milliseconds to wait for the response from 103 // the server. The default value of -1 indicates that it should be inherited 104 // from the associated connection. 105 private long responseTimeout; 106 107 // The referral connector to use when following referrals. 108 @Nullable private ReferralConnector referralConnector; 109 110 111 112 /** 113 * Creates a new LDAP request with the provided set of controls. 114 * 115 * @param controls The set of controls to include in this LDAP request. 116 */ 117 protected LDAPRequest(@Nullable final Control[] controls) 118 { 119 if (controls == null) 120 { 121 this.controls = NO_CONTROLS; 122 } 123 else 124 { 125 this.controls = controls; 126 } 127 128 followReferrals = null; 129 responseTimeout = -1L; 130 intermediateResponseListener = null; 131 referralConnector = null; 132 } 133 134 135 136 /** 137 * {@inheritDoc} 138 */ 139 @Override() 140 @NotNull() 141 public final Control[] getControls() 142 { 143 return controls; 144 } 145 146 147 148 /** 149 * {@inheritDoc} 150 */ 151 @Override() 152 @NotNull() 153 public final List<Control> getControlList() 154 { 155 return Collections.unmodifiableList(Arrays.asList(controls)); 156 } 157 158 159 160 /** 161 * {@inheritDoc} 162 */ 163 @Override() 164 public final boolean hasControl() 165 { 166 return (controls.length > 0); 167 } 168 169 170 171 /** 172 * {@inheritDoc} 173 */ 174 @Override() 175 public final boolean hasControl(@NotNull final String oid) 176 { 177 Validator.ensureNotNull(oid); 178 179 for (final Control c : controls) 180 { 181 if (c.getOID().equals(oid)) 182 { 183 return true; 184 } 185 } 186 187 return false; 188 } 189 190 191 192 /** 193 * {@inheritDoc} 194 */ 195 @Override() 196 @Nullable() 197 public final Control getControl(@NotNull final String oid) 198 { 199 Validator.ensureNotNull(oid); 200 201 for (final Control c : controls) 202 { 203 if (c.getOID().equals(oid)) 204 { 205 return c; 206 } 207 } 208 209 return null; 210 } 211 212 213 214 /** 215 * Updates the set of controls associated with this request. This must only 216 * be called by {@link UpdatableLDAPRequest}. 217 * 218 * @param controls The set of controls to use for this request. 219 */ 220 final void setControlsInternal(@NotNull final Control[] controls) 221 { 222 this.controls = controls; 223 } 224 225 226 227 /** 228 * {@inheritDoc} 229 */ 230 @Override() 231 public final long getResponseTimeoutMillis( 232 @Nullable final LDAPConnection connection) 233 { 234 if ((responseTimeout < 0L) && (connection != null)) 235 { 236 if (this instanceof ExtendedRequest) 237 { 238 final ExtendedRequest extendedRequest = (ExtendedRequest) this; 239 return connection.getConnectionOptions(). 240 getExtendedOperationResponseTimeoutMillis( 241 extendedRequest.getOID()); 242 } 243 else 244 { 245 return connection.getConnectionOptions().getResponseTimeoutMillis( 246 getOperationType()); 247 } 248 } 249 else 250 { 251 return responseTimeout; 252 } 253 } 254 255 256 257 /** 258 * Specifies the maximum length of time in milliseconds that processing on 259 * this operation should be allowed to block while waiting for a response 260 * from the server. A value of zero indicates that no timeout should be 261 * enforced. A value that is less than zero indicates that the default 262 * response timeout for the underlying connection should be used. 263 * 264 * @param responseTimeout The maximum length of time in milliseconds that 265 * processing on this operation should be allowed to 266 * block while waiting for a response from the 267 * server. 268 */ 269 public final void setResponseTimeoutMillis(final long responseTimeout) 270 { 271 if (responseTimeout < 0L) 272 { 273 this.responseTimeout = -1L; 274 } 275 else 276 { 277 this.responseTimeout = responseTimeout; 278 } 279 } 280 281 282 283 /** 284 * Indicates whether to automatically follow any referrals encountered while 285 * processing this request. If a value has been set for this request, then it 286 * will be returned. Otherwise, the default from the connection options for 287 * the provided connection will be used. 288 * 289 * @param connection The connection whose connection options may be used in 290 * the course of making the determination. It must not 291 * be {@code null}. 292 * 293 * @return {@code true} if any referrals encountered during processing should 294 * be automatically followed, or {@code false} if not. 295 */ 296 @Override() 297 public final boolean followReferrals(@NotNull final LDAPConnection connection) 298 { 299 if (followReferrals == null) 300 { 301 return connection.getConnectionOptions().followReferrals(); 302 } 303 else 304 { 305 return followReferrals; 306 } 307 } 308 309 310 311 /** 312 * Indicates whether automatic referral following is enabled for this request. 313 * 314 * @return {@code Boolean.TRUE} if automatic referral following is enabled 315 * for this request, {@code Boolean.FALSE} if not, or {@code null} if 316 * a per-request behavior is not specified. 317 */ 318 @Nullable() 319 final Boolean followReferralsInternal() 320 { 321 return followReferrals; 322 } 323 324 325 326 /** 327 * Specifies whether to automatically follow any referrals encountered while 328 * processing this request. This may be used to override the default behavior 329 * defined in the connection options for the connection used to process the 330 * request. 331 * 332 * @param followReferrals Indicates whether to automatically follow any 333 * referrals encountered while processing this 334 * request. It may be {@code null} to indicate that 335 * the determination should be based on the 336 * connection options for the connection used to 337 * process the request. 338 */ 339 public final void setFollowReferrals(@Nullable final Boolean followReferrals) 340 { 341 this.followReferrals = followReferrals; 342 } 343 344 345 346 /** 347 * {@inheritDoc} 348 */ 349 @Override() 350 @NotNull() 351 public final ReferralConnector getReferralConnector( 352 @NotNull final LDAPConnection connection) 353 { 354 if (referralConnector == null) 355 { 356 return connection.getReferralConnector(); 357 } 358 else 359 { 360 return referralConnector; 361 } 362 } 363 364 365 366 /** 367 * Retrieves the referral connector that has been set for this request. 368 * 369 * @return The referral connector that has been set for this request, or 370 * {@code null} if no referral connector has been set for this 371 * request and the connection's default referral connector will be 372 * used if necessary. 373 */ 374 @Nullable() 375 final ReferralConnector getReferralConnectorInternal() 376 { 377 return referralConnector; 378 } 379 380 381 382 /** 383 * Sets the referral connector that should be used to establish connections 384 * for the purpose of following any referrals encountered when processing this 385 * request. 386 * 387 * @param referralConnector The referral connector that should be used to 388 * establish connections for the purpose of 389 * following any referral encountered when 390 * processing this request. It may be 391 * {@code null} to use the default referral handler 392 * for the connection on which the referral was 393 * received. 394 */ 395 public final void setReferralConnector( 396 @Nullable final ReferralConnector referralConnector) 397 { 398 this.referralConnector = referralConnector; 399 } 400 401 402 403 /** 404 * Retrieves the intermediate response listener for this request, if any. 405 * 406 * @return The intermediate response listener for this request, or 407 * {@code null} if there is none. 408 */ 409 @Nullable() 410 public final IntermediateResponseListener getIntermediateResponseListener() 411 { 412 return intermediateResponseListener; 413 } 414 415 416 417 /** 418 * Sets the intermediate response listener for this request. 419 * 420 * @param listener The intermediate response listener for this request. It 421 * may be {@code null} to clear any existing listener. 422 */ 423 public final void setIntermediateResponseListener( 424 @Nullable final IntermediateResponseListener listener) 425 { 426 intermediateResponseListener = listener; 427 } 428 429 430 431 /** 432 * Processes this operation using the provided connection and returns the 433 * result. 434 * 435 * @param connection The connection to use to process the request. 436 * @param depth The current referral depth for this request. It should 437 * always be one for the initial request, and should only 438 * be incremented when following referrals. 439 * 440 * @return The result of processing this operation. 441 * 442 * @throws LDAPException If a problem occurs while processing the request. 443 */ 444 @InternalUseOnly() 445 @NotNull() 446 protected abstract LDAPResult process(@NotNull LDAPConnection connection, 447 int depth) 448 throws LDAPException; 449 450 451 452 /** 453 * Retrieves the message ID for the last LDAP message sent using this request. 454 * 455 * @return The message ID for the last LDAP message sent using this request, 456 * or -1 if it no LDAP messages have yet been sent using this 457 * request. 458 */ 459 public abstract int getLastMessageID(); 460 461 462 463 /** 464 * Retrieves the type of operation that is represented by this request. 465 * 466 * @return The type of operation that is represented by this request. 467 */ 468 @NotNull() 469 public abstract OperationType getOperationType(); 470 471 472 473 /** 474 * {@inheritDoc} 475 */ 476 @Override() 477 @NotNull() 478 public String toString() 479 { 480 final StringBuilder buffer = new StringBuilder(); 481 toString(buffer); 482 return buffer.toString(); 483 } 484 485 486 487 /** 488 * {@inheritDoc} 489 */ 490 @Override() 491 public abstract void toString(@NotNull StringBuilder buffer); 492}