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    import com.unboundid.util.Extensible;
031    import com.unboundid.util.ThreadSafety;
032    import com.unboundid.util.ThreadSafetyLevel;
033    
034    import 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)
059    public 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    }