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