001/*
002 * Copyright 2007-2014 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2008-2014 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 */
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  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}