001/*
002 * Copyright 2007-2022 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2007-2022 Ping Identity Corporation
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *    http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020/*
021 * Copyright (C) 2007-2022 Ping Identity Corporation
022 *
023 * This program is free software; you can redistribute it and/or modify
024 * it under the terms of the GNU General Public License (GPLv2 only)
025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
026 * as published by the Free Software Foundation.
027 *
028 * This program is distributed in the hope that it will be useful,
029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
031 * GNU General Public License for more details.
032 *
033 * You should have received a copy of the GNU General Public License
034 * along with this program; if not, see <http://www.gnu.org/licenses>.
035 */
036package com.unboundid.ldap.sdk;
037
038
039
040import java.util.Arrays;
041import java.util.Collections;
042import java.util.List;
043
044import com.unboundid.util.Extensible;
045import com.unboundid.util.InternalUseOnly;
046import com.unboundid.util.NotNull;
047import com.unboundid.util.Nullable;
048import com.unboundid.util.ThreadSafety;
049import com.unboundid.util.ThreadSafetyLevel;
050import com.unboundid.util.Validator;
051
052
053
054/**
055 * This class provides a framework that should be extended by all types of LDAP
056 * requests.  It provides methods for interacting with the set of controls to
057 * include as part of the request and configuring a response timeout, which is
058 * the maximum length of time that the SDK should wait for a response to the
059 * request before returning an error back to the caller.
060 * <BR><BR>
061 * {@code LDAPRequest} objects are not immutable and should not be considered
062 * threadsafe.  A single {@code LDAPRequest} object instance should not be used
063 * concurrently by multiple threads, but instead each thread wishing to process
064 * a request should have its own instance of that request.  The
065 * {@link #duplicate()} method may be used to create an exact copy of a request
066 * suitable for processing by a separate thread.
067 * <BR><BR>
068 * Note that even though this class is marked with the @Extensible annotation
069 * type, it should not be directly subclassed by third-party code.  Only the
070 * {@link ExtendedRequest} and {@link SASLBindRequest} subclasses are actually
071 * intended to be extended by third-party code.
072 */
073@Extensible()
074@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
075public abstract class LDAPRequest
076       implements ReadOnlyLDAPRequest
077{
078  /**
079   * The set of controls that will be used if none were provided.
080   */
081  @NotNull static final Control[] NO_CONTROLS = new Control[0];
082
083
084
085  /**
086   * The serial version UID for this serializable class.
087   */
088  private static final long serialVersionUID = -2040756188243320117L;
089
090
091
092  // Indicates whether to automatically follow referrals returned while
093  // processing this request.
094  @Nullable private Boolean followReferrals;
095
096  // The set of controls for this request.
097  @NotNull private Control[] controls;
098
099  // The intermediate response listener for this request.
100  @Nullable private IntermediateResponseListener intermediateResponseListener;
101
102  // The maximum length of time in milliseconds to wait for the response from
103  // the server.  The default value of -1 indicates that it should be inherited
104  // from the associated connection.
105  private long responseTimeout;
106
107  // The referral connector to use when following referrals.
108  @Nullable private ReferralConnector referralConnector;
109
110
111
112  /**
113   * Creates a new LDAP request with the provided set of controls.
114   *
115   * @param  controls  The set of controls to include in this LDAP request.
116   */
117  protected LDAPRequest(@Nullable final Control[] controls)
118  {
119    if (controls == null)
120    {
121      this.controls = NO_CONTROLS;
122    }
123    else
124    {
125      this.controls = controls;
126    }
127
128    followReferrals = null;
129    responseTimeout = -1L;
130    intermediateResponseListener = null;
131    referralConnector = null;
132  }
133
134
135
136  /**
137   * {@inheritDoc}
138   */
139  @Override()
140  @NotNull()
141  public final Control[] getControls()
142  {
143    return controls;
144  }
145
146
147
148  /**
149   * {@inheritDoc}
150   */
151  @Override()
152  @NotNull()
153  public final List<Control> getControlList()
154  {
155    return Collections.unmodifiableList(Arrays.asList(controls));
156  }
157
158
159
160  /**
161   * {@inheritDoc}
162   */
163  @Override()
164  public final boolean hasControl()
165  {
166    return (controls.length > 0);
167  }
168
169
170
171  /**
172   * {@inheritDoc}
173   */
174  @Override()
175  public final boolean hasControl(@NotNull final String oid)
176  {
177    Validator.ensureNotNull(oid);
178
179    for (final Control c : controls)
180    {
181      if (c.getOID().equals(oid))
182      {
183        return true;
184      }
185    }
186
187    return false;
188  }
189
190
191
192  /**
193   * {@inheritDoc}
194   */
195  @Override()
196  @Nullable()
197  public final Control getControl(@NotNull final String oid)
198  {
199    Validator.ensureNotNull(oid);
200
201    for (final Control c : controls)
202    {
203      if (c.getOID().equals(oid))
204      {
205        return c;
206      }
207    }
208
209    return null;
210  }
211
212
213
214  /**
215   * Updates the set of controls associated with this request.  This must only
216   * be called by {@link UpdatableLDAPRequest}.
217   *
218   * @param  controls  The set of controls to use for this request.
219   */
220  final void setControlsInternal(@NotNull final Control[] controls)
221  {
222    this.controls = controls;
223  }
224
225
226
227  /**
228   * {@inheritDoc}
229   */
230  @Override()
231  public final long getResponseTimeoutMillis(
232                         @Nullable final LDAPConnection connection)
233  {
234    if ((responseTimeout < 0L) && (connection != null))
235    {
236      if (this instanceof ExtendedRequest)
237      {
238        final ExtendedRequest extendedRequest = (ExtendedRequest) this;
239        return connection.getConnectionOptions().
240             getExtendedOperationResponseTimeoutMillis(
241                  extendedRequest.getOID());
242      }
243      else
244      {
245        return connection.getConnectionOptions().getResponseTimeoutMillis(
246             getOperationType());
247      }
248    }
249    else
250    {
251      return responseTimeout;
252    }
253  }
254
255
256
257  /**
258   * Specifies the maximum length of time in milliseconds that processing on
259   * this operation should be allowed to block while waiting for a response
260   * from the server.  A value of zero indicates that no timeout should be
261   * enforced.  A value that is less than zero indicates that the default
262   * response timeout for the underlying connection should be used.
263   *
264   * @param  responseTimeout  The maximum length of time in milliseconds that
265   *                          processing on this operation should be allowed to
266   *                          block while waiting for a response from the
267   *                          server.
268   */
269  public final void setResponseTimeoutMillis(final long responseTimeout)
270  {
271    if (responseTimeout < 0L)
272    {
273      this.responseTimeout = -1L;
274    }
275    else
276    {
277      this.responseTimeout = responseTimeout;
278    }
279  }
280
281
282
283  /**
284   * Indicates whether to automatically follow any referrals encountered while
285   * processing this request.  If a value has been set for this request, then it
286   * will be returned.  Otherwise, the default from the connection options for
287   * the provided connection will be used.
288   *
289   * @param  connection  The connection whose connection options may be used in
290   *                     the course of making the determination.  It must not
291   *                     be {@code null}.
292   *
293   * @return  {@code true} if any referrals encountered during processing should
294   *          be automatically followed, or {@code false} if not.
295   */
296  @Override()
297  public final boolean followReferrals(@NotNull final LDAPConnection connection)
298  {
299    if (followReferrals == null)
300    {
301      return connection.getConnectionOptions().followReferrals();
302    }
303    else
304    {
305      return followReferrals;
306    }
307  }
308
309
310
311  /**
312   * Indicates whether automatic referral following is enabled for this request.
313   *
314   * @return  {@code Boolean.TRUE} if automatic referral following is enabled
315   *          for this request, {@code Boolean.FALSE} if not, or {@code null} if
316   *          a per-request behavior is not specified.
317   */
318  @Nullable()
319  final Boolean followReferralsInternal()
320  {
321    return followReferrals;
322  }
323
324
325
326  /**
327   * Specifies whether to automatically follow any referrals encountered while
328   * processing this request.  This may be used to override the default behavior
329   * defined in the connection options for the connection used to process the
330   * request.
331   *
332   * @param  followReferrals  Indicates whether to automatically follow any
333   *                          referrals encountered while processing this
334   *                          request.  It may be {@code null} to indicate that
335   *                          the determination should be based on the
336   *                          connection options for the connection used to
337   *                          process the request.
338   */
339  public final void setFollowReferrals(@Nullable final Boolean followReferrals)
340  {
341    this.followReferrals = followReferrals;
342  }
343
344
345
346  /**
347   * {@inheritDoc}
348   */
349  @Override()
350  @NotNull()
351  public final ReferralConnector getReferralConnector(
352                                      @NotNull final LDAPConnection connection)
353  {
354    if (referralConnector == null)
355    {
356      return connection.getReferralConnector();
357    }
358    else
359    {
360      return referralConnector;
361    }
362  }
363
364
365
366  /**
367   * Retrieves the referral connector that has been set for this request.
368   *
369   * @return  The referral connector that has been set for this request, or
370   *          {@code null} if no referral connector has been set for this
371   *          request and the connection's default referral connector will be
372   *          used if necessary.
373   */
374  @Nullable()
375  final ReferralConnector getReferralConnectorInternal()
376  {
377    return referralConnector;
378  }
379
380
381
382  /**
383   * Sets the referral connector that should be used to establish connections
384   * for the purpose of following any referrals encountered when processing this
385   * request.
386   *
387   * @param  referralConnector  The referral connector that should be used to
388   *                            establish connections for the purpose of
389   *                            following any referral encountered when
390   *                            processing this request.  It may be
391   *                            {@code null} to use the default referral handler
392   *                            for the connection on which the referral was
393   *                            received.
394   */
395  public final void setReferralConnector(
396                         @Nullable final ReferralConnector referralConnector)
397  {
398    this.referralConnector = referralConnector;
399  }
400
401
402
403  /**
404   * Retrieves the intermediate response listener for this request, if any.
405   *
406   * @return  The intermediate response listener for this request, or
407   *          {@code null} if there is none.
408   */
409  @Nullable()
410  public final IntermediateResponseListener getIntermediateResponseListener()
411  {
412    return intermediateResponseListener;
413  }
414
415
416
417  /**
418   * Sets the intermediate response listener for this request.
419   *
420   * @param  listener  The intermediate response listener for this request.  It
421   *                   may be {@code null} to clear any existing listener.
422   */
423  public final void setIntermediateResponseListener(
424                         @Nullable final IntermediateResponseListener listener)
425  {
426    intermediateResponseListener = listener;
427  }
428
429
430
431  /**
432   * Processes this operation using the provided connection and returns the
433   * result.
434   *
435   * @param  connection  The connection to use to process the request.
436   * @param  depth       The current referral depth for this request.  It should
437   *                     always be one for the initial request, and should only
438   *                     be incremented when following referrals.
439   *
440   * @return  The result of processing this operation.
441   *
442   * @throws  LDAPException  If a problem occurs while processing the request.
443   */
444  @InternalUseOnly()
445  @NotNull()
446  protected abstract LDAPResult process(@NotNull LDAPConnection connection,
447                                        int depth)
448            throws LDAPException;
449
450
451
452  /**
453   * Retrieves the message ID for the last LDAP message sent using this request.
454   *
455   * @return  The message ID for the last LDAP message sent using this request,
456   *          or -1 if it no LDAP messages have yet been sent using this
457   *          request.
458   */
459  public abstract int getLastMessageID();
460
461
462
463  /**
464   * Retrieves the type of operation that is represented by this request.
465   *
466   * @return  The type of operation that is represented by this request.
467   */
468  @NotNull()
469  public abstract OperationType getOperationType();
470
471
472
473  /**
474   * {@inheritDoc}
475   */
476  @Override()
477  @NotNull()
478  public String toString()
479  {
480    final StringBuilder buffer = new StringBuilder();
481    toString(buffer);
482    return buffer.toString();
483  }
484
485
486
487  /**
488   * {@inheritDoc}
489   */
490  @Override()
491  public abstract void toString(@NotNull StringBuilder buffer);
492}