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