001/*
002 * Copyright 2007-2017 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2008-2017 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      return connection.getConnectionOptions().getResponseTimeoutMillis();
215    }
216    else
217    {
218      return responseTimeout;
219    }
220  }
221
222
223
224  /**
225   * Specifies the maximum length of time in milliseconds that processing on
226   * this operation should be allowed to block while waiting for a response
227   * from the server.  A value of zero indicates that no timeout should be
228   * enforced.  A value that is less than zero indicates that the default
229   * response timeout for the underlying connection should be used.
230   *
231   * @param  responseTimeout  The maximum length of time in milliseconds that
232   *                          processing on this operation should be allowed to
233   *                          block while waiting for a response from the
234   *                          server.
235   */
236  public final void setResponseTimeoutMillis(final long responseTimeout)
237  {
238    if (responseTimeout < 0L)
239    {
240      this.responseTimeout = -1L;
241    }
242    else
243    {
244      this.responseTimeout = responseTimeout;
245    }
246  }
247
248
249
250  /**
251   * Indicates whether to automatically follow any referrals encountered while
252   * processing this request.  If a value has been set for this request, then it
253   * will be returned.  Otherwise, the default from the connection options for
254   * the provided connection will be used.
255   *
256   * @param  connection  The connection whose connection options may be used in
257   *                     the course of making the determination.  It must not
258   *                     be {@code null}.
259   *
260   * @return  {@code true} if any referrals encountered during processing should
261   *          be automatically followed, or {@code false} if not.
262   */
263  @Override()
264  public final boolean followReferrals(final LDAPConnection connection)
265  {
266    if (followReferrals == null)
267    {
268      return connection.getConnectionOptions().followReferrals();
269    }
270    else
271    {
272      return followReferrals;
273    }
274  }
275
276
277
278  /**
279   * Indicates whether automatic referral following is enabled for this request.
280   *
281   * @return  {@code Boolean.TRUE} if automatic referral following is enabled
282   *          for this request, {@code Boolean.FALSE} if not, or {@code null} if
283   *          a per-request behavior is not specified.
284   */
285  final Boolean followReferralsInternal()
286  {
287    return followReferrals;
288  }
289
290
291
292  /**
293   * Specifies whether to automatically follow any referrals encountered while
294   * processing this request.  This may be used to override the default behavior
295   * defined in the connection options for the connection used to process the
296   * request.
297   *
298   * @param  followReferrals  Indicates whether to automatically follow any
299   *                          referrals encountered while processing this
300   *                          request.  It may be {@code null} to indicate that
301   *                          the determination should be based on the
302   *                          connection options for the connection used to
303   *                          process the request.
304   */
305  public final void setFollowReferrals(final Boolean followReferrals)
306  {
307    this.followReferrals = followReferrals;
308  }
309
310
311
312  /**
313   * Retrieves the intermediate response listener for this request, if any.
314   *
315   * @return  The intermediate response listener for this request, or
316   *          {@code null} if there is none.
317   */
318  public final IntermediateResponseListener getIntermediateResponseListener()
319  {
320    return intermediateResponseListener;
321  }
322
323
324
325  /**
326   * Sets the intermediate response listener for this request.
327   *
328   * @param  listener  The intermediate response listener for this request.  It
329   *                   may be {@code null} to clear any existing listener.
330   */
331  public final void setIntermediateResponseListener(
332                         final IntermediateResponseListener listener)
333  {
334    intermediateResponseListener = listener;
335  }
336
337
338
339  /**
340   * Processes this operation using the provided connection and returns the
341   * result.
342   *
343   * @param  connection  The connection to use to process the request.
344   * @param  depth       The current referral depth for this request.  It should
345   *                     always be one for the initial request, and should only
346   *                     be incremented when following referrals.
347   *
348   * @return  The result of processing this operation.
349   *
350   * @throws  LDAPException  If a problem occurs while processing the request.
351   */
352  @InternalUseOnly()
353  protected abstract LDAPResult process(LDAPConnection connection, int depth)
354            throws LDAPException;
355
356
357
358  /**
359   * Retrieves the message ID for the last LDAP message sent using this request.
360   *
361   * @return  The message ID for the last LDAP message sent using this request,
362   *          or -1 if it no LDAP messages have yet been sent using this
363   *          request.
364   */
365  public abstract int getLastMessageID();
366
367
368
369  /**
370   * Retrieves the type of operation that is represented by this request.
371   *
372   * @return  The type of operation that is represented by this request.
373   */
374  public abstract OperationType getOperationType();
375
376
377
378  /**
379   * {@inheritDoc}
380   */
381  @Override()
382  public String toString()
383  {
384    final StringBuilder buffer = new StringBuilder();
385    toString(buffer);
386    return buffer.toString();
387  }
388
389
390
391  /**
392   * {@inheritDoc}
393   */
394  @Override()
395  public abstract void toString(StringBuilder buffer);
396}