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