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