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
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 }