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