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