001    /*
002     * Copyright 2014-2015 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 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.unboundidds.controls;
022    
023    
024    
025    import java.util.ArrayList;
026    
027    import com.unboundid.asn1.ASN1Boolean;
028    import com.unboundid.asn1.ASN1Element;
029    import com.unboundid.asn1.ASN1Integer;
030    import com.unboundid.asn1.ASN1OctetString;
031    import com.unboundid.asn1.ASN1Sequence;
032    import com.unboundid.ldap.sdk.Control;
033    import com.unboundid.ldap.sdk.LDAPException;
034    import com.unboundid.ldap.sdk.ResultCode;
035    import com.unboundid.util.Debug;
036    import com.unboundid.util.NotMutable;
037    import com.unboundid.util.StaticUtils;
038    import com.unboundid.util.ThreadSafety;
039    import com.unboundid.util.ThreadSafetyLevel;
040    import com.unboundid.util.Validator;
041    
042    import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*;
043    
044    
045    
046    /**
047     * <BLOCKQUOTE>
048     *   <B>NOTE:</B>  This class is part of the Commercial Edition of the UnboundID
049     *   LDAP SDK for Java.  It is not available for use in applications that
050     *   include only the Standard Edition of the LDAP SDK, and is not supported for
051     *   use in conjunction with non-UnboundID products.
052     * </BLOCKQUOTE>
053     * This class provides a request control which may be included in a search
054     * request to indicate that the server should provide the number of entries that
055     * match the search criteria.  The count will be included in the search result
056     * done message, and all search result entries will be suppressed.
057     * <BR><BR>
058     * Whenever possible, the server will use index information to quickly identify
059     * entries matching the criteria of the associated search request.  However, if
060     * the count is only determined using index information, then that count may
061     * include entries that would not actually be returned to the client in the
062     * course of processing that search (e.g., because the client doesn't have
063     * permission to access the entry, or because it is a special "operational"
064     * entry like an LDAP subentry, replication conflict entry, or soft-deleted
065     * entry).  Indicating that the server should always examine candidate entries
066     * will increase the length of time to obtain the matching entry count, but will
067     * ensure that the count will not include entries that would not otherwise be
068     * returned by that search.
069     * <BR><BR>
070     * Also note that this control is not compatible for use with other controls
071     * that may cause only a subset of entries to be returned, including the simple
072     * paged results control and the virtual list view control.  It is also not
073     * compatible for use with other controls that may cause the server to return
074     * more entries than those that match the search criteria, like the LDAP join
075     * control.
076     * <BR><BR>
077     * The OID for a matching entry count request control is
078     * "1.3.6.1.4.1.30221.2.5.36", and it may have a criticality of either
079     * {@code true} or {@code false}.  It must include a value with the following
080     * encoding:
081     * <PRE>
082     *   MatchingEntryCountRequest ::= SEQUENCE {
083     *        maxCandidatesToExamine       [0] INTEGER (0 .. MAX) DEFAULT 0,
084     *        alwaysExamineCandidates      [1] BOOLEAN DEFAULT FALSE,
085     *        processSearchIfUnindexed     [2] BOOLEAN DEFAULT FALSE,
086     *        includeDebugInfo             [3] BOOLEAN DEFAULT FALSE,
087     *        ... }
088     * </PRE>
089     *
090     * @see  MatchingEntryCountResponseControl
091     */
092    @NotMutable()
093    @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
094    public final class MatchingEntryCountRequestControl
095           extends Control
096    {
097      /**
098       * The OID (1.3.6.1.4.1.30221.2.5.36) for the matching entry count request
099       * control.
100       */
101      public static final String MATCHING_ENTRY_COUNT_REQUEST_OID =
102           "1.3.6.1.4.1.30221.2.5.36";
103    
104    
105    
106      /**
107       * The BER type for the element that specifies the maximum number of candidate
108       * entries to examine.
109       */
110      private static final byte TYPE_MAX_CANDIDATES_TO_EXAMINE = (byte) 0x80;
111    
112    
113    
114      /**
115       * The BER type for the element that indicates whether always examine
116       * candidate entries to determine whether they would actually be returned to
117       * the client.
118       */
119      private static final byte TYPE_ALWAYS_EXAMINE_CANDIDATES = (byte) 0x81;
120    
121    
122    
123      /**
124       * The BER type for the element that indicates whether to process an unindexed
125       * search to determine the number of matching entries.
126       */
127      private static final byte TYPE_PROCESS_SEARCH_IF_UNINDEXED = (byte) 0x82;
128    
129    
130    
131      /**
132       * The BER type for the element that indicates whether to include debug
133       * information in the response.
134       */
135      private static final byte TYPE_INCLUDE_DEBUG_INFO = (byte) 0x83;
136    
137    
138    
139      /**
140       * The serial version UID for this serializable class.
141       */
142      private static final long serialVersionUID = -6077150575658563941L;
143    
144    
145    
146      // Indicates whether the server should internally retrieve and examine
147      // candidate entries to determine whether they would actually be returned to
148      // the client.
149      private final boolean alwaysExamineCandidates;
150    
151      // Indicates whether to include debug information in the response control.
152      private final boolean includeDebugInfo;
153    
154      // Indicates whether the server should attempt to actually iterate through the
155      // entries in the backend in order to obtain the count if the search criteria
156      // is not indexed.
157      private final boolean processSearchIfUnindexed;
158    
159      // The maximum number of candidate entries that should be examined if it is
160      // not possible to obtain an exact count using only information contained in
161      // the server indexes.
162      private final int maxCandidatesToExamine;
163    
164    
165    
166      /**
167       * Creates a new matching entry count request control with the default
168       * settings.  The control will be critical, no candidate entries will be
169       * examined, and the search will not be processed if it is unindexed.
170       */
171      public MatchingEntryCountRequestControl()
172      {
173        this(true, 0, false, false, false);
174      }
175    
176    
177    
178      /**
179       * Creates a new matching entry count request control with the provided
180       * information.
181       *
182       * @param  isCritical                Indicates whether this control should be
183       *                                   critical.
184       * @param  maxCandidatesToExamine    The maximum number of candidate entries
185       *                                   that the server should retrieve and
186       *                                   examine to determine whether they
187       *                                   actually match the search criteria.  If
188       *                                   the search is partially indexed and the
189       *                                   total number of candidate entries is less
190       *                                   than or equal to this value, then these
191       *                                   candidate entries will be examined to
192       *                                   determine which of them match the search
193       *                                   criteria so that an accurate count can
194       *                                   be determined.  If the search is fully
195       *                                   indexed such that the all candidate
196       *                                   entries are known to match the search
197       *                                   criteria, then the server may still
198       *                                   examine each of these entries if the
199       *                                   number of candidates is less than
200       *                                   {@code maxCandidatesToExamine} and
201       *                                   {@code alwaysExamineCandidates} is true
202       *                                   in order to allow the entry count that
203       *                                   is returned to be restricted to only
204       *                                   those entries that would actually be
205       *                                   returned to the client.  This will be
206       *                                   ignored for searches that are completely
207       *                                   unindexed.
208       *                                   <BR><BR>
209       *                                   The value for this argument must be
210       *                                   greater than or equal to zero.  If it
211       *                                   is zero, then the server will not
212       *                                   examine any entries, so a
213       *                                   partially-indexed search will only be
214       *                                   able to return a count that is an upper
215       *                                   bound, and a fully-indexed search will
216       *                                   only be able to return an unexamined
217       *                                   exact count.  If there should be no bound
218       *                                   on the number of entries to retrieve,
219       *                                   then a value of {@code Integer.MAX_VALUE}
220       *                                   may be specified.
221       * @param  alwaysExamineCandidates   Indicates whether the server should
222       *                                   always examine candidate entries to
223       *                                   determine whether they would actually
224       *                                   be returned to the client in a normal
225       *                                   search.  This will only be used for
226       *                                   fully-indexed searches in which the
227       *                                   set of matching entries is known.  If the
228       *                                   value is {@code true} and the number of
229       *                                   candidates is smaller than
230       *                                   {@code maxCandidatesToExamine}, then each
231       *                                   matching entry will be internally
232       *                                   retrieved and examined to determine
233       *                                   whether it would be returned to the
234       *                                   client based on the details of the search
235       *                                   request (e.g., whether the requester has
236       *                                   permission to access the entry, whether
237       *                                   it's an LDAP subentry, replication
238       *                                   conflict entry, soft-deleted entry, or
239       *                                   other type of entry that is normally
240       *                                   hidden) so that an exact count can be
241       *                                   returned.  If this is {@code false} or
242       *                                   the number of candidates exceeds
243       *                                   {@code maxCandidatesToExamine}, then the
244       *                                   server will only be able to return an
245       *                                   unexamined count which may include
246       *                                   entries that match the search criteria
247       *                                   but that would not normally be returned
248       *                                   to the requester.
249       * @param  processSearchIfUnindexed  Indicates whether the server should
250       *                                   attempt to determine the number of
251       *                                   matching entries if the search criteria
252       *                                   is completely unindexed.  If this is
253       *                                   {@code true} and the requester has the
254       *                                   unindexed-search privilege, then the
255       *                                   server will iterate through all entries
256       *                                   in the scope (which may take a very long
257       *                                   time to complete) in order to to
258       *                                   determine which of them match the search
259       *                                   criteria so that it can return an
260       *                                   accurate count.  If this is
261       *                                   {@code false} or the requester does not
262       *                                   have the unindexed-search privilege, then
263       *                                   the server will not spend any time
264       *                                   attempting to determine the number of
265       *                                   matching entries and will instead return
266       *                                   a matching entry count response control
267       *                                   indicating that the entry count is
268       *                                   unknown.
269       * @param  includeDebugInfo          Indicates whether the server should
270       *                                   include debug information in the response
271       *                                   that may help better understand how it
272       *                                   arrived at the result.  If any debug
273       *                                   information is returned, it will be in
274       *                                   the form of human-readable text that is
275       *                                   not intended to be machine-parsable.
276       */
277      public MatchingEntryCountRequestControl(final boolean isCritical,
278                  final int maxCandidatesToExamine,
279                  final boolean alwaysExamineCandidates,
280                  final boolean processSearchIfUnindexed,
281                  final boolean includeDebugInfo)
282      {
283        super(MATCHING_ENTRY_COUNT_REQUEST_OID, isCritical,
284             encodeValue(maxCandidatesToExamine, alwaysExamineCandidates,
285                  processSearchIfUnindexed, includeDebugInfo));
286    
287        Validator.ensureTrue(maxCandidatesToExamine >= 0);
288    
289        this.maxCandidatesToExamine   = maxCandidatesToExamine;
290        this.alwaysExamineCandidates  = alwaysExamineCandidates;
291        this.processSearchIfUnindexed = processSearchIfUnindexed;
292        this.includeDebugInfo         = includeDebugInfo;
293      }
294    
295    
296    
297      /**
298       * Creates a new matching entry count request control that is decoded from the
299       * provided generic control.
300       *
301       * @param  control  The control to decode as a matching entry count request
302       *                  control.
303       *
304       * @throws  LDAPException  If the provided control cannot be decoded as a
305       *                         matching entry count request control.
306       */
307      public MatchingEntryCountRequestControl(final Control control)
308             throws LDAPException
309      {
310        super(control);
311    
312        final ASN1OctetString value = control.getValue();
313        if (value == null)
314        {
315          throw new LDAPException(ResultCode.DECODING_ERROR,
316               ERR_MATCHING_ENTRY_COUNT_REQUEST_MISSING_VALUE.get());
317        }
318    
319        try
320        {
321          boolean alwaysExamine    = false;
322          boolean debug            = false;
323          boolean processUnindexed = false;
324          int     maxCandidates    = 0;
325          final ASN1Element[] elements =
326               ASN1Sequence.decodeAsSequence(value.getValue()).elements();
327          for (final ASN1Element e : elements)
328          {
329            switch (e.getType())
330            {
331              case TYPE_MAX_CANDIDATES_TO_EXAMINE:
332                maxCandidates = ASN1Integer.decodeAsInteger(e).intValue();
333                if (maxCandidates < 0)
334                {
335                  throw new LDAPException(ResultCode.DECODING_ERROR,
336                       ERR_MATCHING_ENTRY_COUNT_REQUEST_INVALID_MAX.get());
337                }
338                break;
339    
340              case TYPE_ALWAYS_EXAMINE_CANDIDATES:
341                alwaysExamine = ASN1Boolean.decodeAsBoolean(e).booleanValue();
342                break;
343    
344              case TYPE_PROCESS_SEARCH_IF_UNINDEXED:
345                processUnindexed = ASN1Boolean.decodeAsBoolean(e).booleanValue();
346                break;
347    
348              case TYPE_INCLUDE_DEBUG_INFO:
349                debug = ASN1Boolean.decodeAsBoolean(e).booleanValue();
350                break;
351    
352              default:
353                throw new LDAPException(ResultCode.DECODING_ERROR,
354                     ERR_MATCHING_ENTRY_COUNT_REQUEST_INVALID_ELEMENT_TYPE.get(
355                          StaticUtils.toHex(e.getType())));
356            }
357          }
358    
359          maxCandidatesToExamine   = maxCandidates;
360          alwaysExamineCandidates  = alwaysExamine;
361          processSearchIfUnindexed = processUnindexed;
362          includeDebugInfo         = debug;
363        }
364        catch (final LDAPException le)
365        {
366          Debug.debugException(le);
367          throw le;
368        }
369        catch (final Exception e)
370        {
371          Debug.debugException(e);
372          throw new LDAPException(ResultCode.DECODING_ERROR,
373               ERR_MATCHING_ENTRY_COUNT_REQUEST_CANNOT_DECODE.get(
374                    StaticUtils.getExceptionMessage(e)),
375               e);
376        }
377      }
378    
379    
380    
381      /**
382       * Encodes the provided information into an ASN.1 octet string suitable for
383       * use as the control value.
384       *
385       * @param  maxCandidatesToExamine    The maximum number of candidate entries
386       *                                   that the server should retrieve and
387       *                                   examine to determine whether they
388       *                                   actually match the search criteria.
389       * @param  alwaysExamineCandidates   Indicates whether the server should
390       *                                   always examine candidate entries to
391       *                                   determine whether they would actually
392       *                                   be returned to the client in a normal
393       *                                   search with the same criteria.
394       * @param  processSearchIfUnindexed  Indicates whether the server should
395       *                                   attempt to determine the number of
396       *                                   matching entries if the search criteria
397       *                                   is completely unindexed.
398       * @param  includeDebugInfo          Indicates whether the server should
399       *                                   include debug information in the response
400       *                                   that may help better understand how it
401       *                                   arrived at the result.
402       *
403       * @return  The ASN.1 octet string containing the encoded control value.
404       */
405      private static ASN1OctetString encodeValue(
406                                          final int maxCandidatesToExamine,
407                                          final boolean alwaysExamineCandidates,
408                                          final boolean processSearchIfUnindexed,
409                                          final boolean includeDebugInfo)
410      {
411        final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(4);
412    
413        if (maxCandidatesToExamine > 0)
414        {
415          elements.add(new ASN1Integer(TYPE_MAX_CANDIDATES_TO_EXAMINE,
416               maxCandidatesToExamine));
417        }
418    
419        if (alwaysExamineCandidates)
420        {
421          elements.add(new ASN1Boolean(TYPE_ALWAYS_EXAMINE_CANDIDATES, true));
422        }
423    
424        if (processSearchIfUnindexed)
425        {
426          elements.add(new ASN1Boolean(TYPE_PROCESS_SEARCH_IF_UNINDEXED, true));
427        }
428    
429        if (includeDebugInfo)
430        {
431          elements.add(new ASN1Boolean(TYPE_INCLUDE_DEBUG_INFO, true));
432        }
433    
434        return new ASN1OctetString(new ASN1Sequence(elements).encode());
435      }
436    
437    
438    
439      /**
440       * Retrieves the maximum number of candidate entries that should be examined
441       * in order to determine accurate count of the number of matching entries.
442       * <BR><BR>
443       * For a fully-indexed search, this property will only be used if
444       * {@link #alwaysExamineCandidates} is true.  If the number of candidate
445       * entries identified is less than the maximum number of candidates to
446       * examine, then the server will return an {@code EXAMINED_COUNT} result that
447       * indicates the number of entries matching the criteria that would actually
448       * be returned in a normal search with the same criteria.  If the number of
449       * candidate entries exceeds the maximum number of candidates to examine, then
450       * the server will return an {@code UNEXAMINED_COUNT} result that indicates
451       * the number of entries matching the search criteria but that may include
452       * entries that would not actually be returned to the client.
453       * <BR><BR>
454       * For a partially-indexed search, if the upper bound on the number of
455       * candidates is less than or equal to the maximum number of candidates to
456       * examine, then the server will internally retrieve and examine each of those
457       * candidates to determine which of them match the search criteria and would
458       * actually be returned to the client, and will then return an
459       * {@code EXAMINED_COUNT} result with that count.  If the upper bound on the
460       * number of candidates is greater than the maximum number of candidates to
461       * examine, then the server will return an {@code UPPER_BOUND} result to
462       * indicate that the exact count is not known but an upper bound is available.
463       *
464       * @return  The maximum number of candidate entries to examine in order to
465       *          determine an accurate count of the number of matching entries.
466       */
467      public int getMaxCandidatesToExamine()
468      {
469        return maxCandidatesToExamine;
470      }
471    
472    
473    
474      /**
475       * Indicates whether the server should always examine candidate entries in
476       * fully-indexed searches to determine whether they would actually be returned
477       * to the client in a normal search with the same criteria.
478       *
479       * @return  {@code true} if the server should attempt to internally retrieve
480       *          and examine matching entries to determine whether they would
481       *          normally be returned to the client (i.e.., that the client has
482       *          permission to access the entry and that it is not a
483       *          normally-hidden entry like an LDAP subentry, a replication
484       *          conflict entry, or a soft-deleted entry), or {@code false} if the
485       *          server should return an unverified count.
486       */
487      public boolean alwaysExamineCandidates()
488      {
489        return alwaysExamineCandidates;
490      }
491    
492    
493    
494      /**
495       * Indicates whether the server should internally retrieve and examine all
496       * entries within the search scope in order to obtain an exact matching entry
497       * count for an unindexed search.  Note that this value will not be considered
498       * for completely-indexed or partially-indexed searches, nor for searches in
499       * which matching entries should be returned.
500       *
501       * @return  {@code true} if the server should internally retrieve and examine
502       *          all entries within the search scope in order to obtain an exact
503       *          matching entry count for an unindexed search, or {@code false} if
504       *          not.
505       */
506      public boolean processSearchIfUnindexed()
507      {
508        return processSearchIfUnindexed;
509      }
510    
511    
512    
513      /**
514       * Indicates whether the server should include debug information in the
515       * response control that provides additional information about how the server
516       * arrived at the result.  If debug information is to be provided, it will be
517       * in a human-readable rather than machine-parsable form.
518       *
519       * @return  {@code true} if the server should include debug information in
520       *          the response control, or {@code false} if not.
521       */
522      public boolean includeDebugInfo()
523      {
524        return includeDebugInfo;
525      }
526    
527    
528    
529      /**
530       * {@inheritDoc}
531       */
532      @Override()
533      public String getControlName()
534      {
535        return INFO_CONTROL_NAME_MATCHING_ENTRY_COUNT_REQUEST.get();
536      }
537    
538    
539    
540      /**
541       * {@inheritDoc}
542       */
543      @Override()
544      public void toString(final StringBuilder buffer)
545      {
546        buffer.append("MatchingEntryCountRequestControl(isCritical=");
547        buffer.append(isCritical());
548        buffer.append(", maxCandidatesToExamine=");
549        buffer.append(maxCandidatesToExamine);
550        buffer.append(", alwaysExamineCandidates=");
551        buffer.append(alwaysExamineCandidates);
552        buffer.append(", processSearchIfUnindexed=");
553        buffer.append(processSearchIfUnindexed);
554        buffer.append(", includeDebugInfo=");
555        buffer.append(includeDebugInfo);
556        buffer.append(')');
557      }
558    }