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