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