001    /*
002     * Copyright 2009-2015 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2009-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.protocol;
022    
023    
024    
025    import java.util.ArrayList;
026    import java.util.Collections;
027    import java.util.Iterator;
028    import java.util.List;
029    
030    import com.unboundid.asn1.ASN1Boolean;
031    import com.unboundid.asn1.ASN1Buffer;
032    import com.unboundid.asn1.ASN1BufferSequence;
033    import com.unboundid.asn1.ASN1Element;
034    import com.unboundid.asn1.ASN1Enumerated;
035    import com.unboundid.asn1.ASN1Integer;
036    import com.unboundid.asn1.ASN1OctetString;
037    import com.unboundid.asn1.ASN1Sequence;
038    import com.unboundid.asn1.ASN1StreamReader;
039    import com.unboundid.asn1.ASN1StreamReaderSequence;
040    import com.unboundid.ldap.sdk.Control;
041    import com.unboundid.ldap.sdk.DereferencePolicy;
042    import com.unboundid.ldap.sdk.Filter;
043    import com.unboundid.ldap.sdk.LDAPException;
044    import com.unboundid.ldap.sdk.ResultCode;
045    import com.unboundid.ldap.sdk.SearchRequest;
046    import com.unboundid.ldap.sdk.SearchScope;
047    import com.unboundid.util.NotMutable;
048    import com.unboundid.util.InternalUseOnly;
049    import com.unboundid.util.ThreadSafety;
050    import com.unboundid.util.ThreadSafetyLevel;
051    
052    import static com.unboundid.ldap.protocol.ProtocolMessages.*;
053    import static com.unboundid.util.Debug.*;
054    import static com.unboundid.util.StaticUtils.*;
055    
056    
057    
058    /**
059     * This class provides an implementation of an LDAP search request protocol op.
060     */
061    @InternalUseOnly()
062    @NotMutable()
063    @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
064    public final class SearchRequestProtocolOp
065           implements ProtocolOp
066    {
067      /**
068       * The serial version UID for this serializable class.
069       */
070      private static final long serialVersionUID = -8521750809606744181L;
071    
072    
073    
074      // The typesOnly flag for this search request.
075      private final boolean typesOnly;
076    
077      // The dereference policy for this search request.
078      private final DereferencePolicy derefPolicy;
079    
080      // The filter for this search request.
081      private final Filter filter;
082    
083      // The size limit for this search request.
084      private final int sizeLimit;
085    
086      // The time limit for this search request.
087      private final int timeLimit;
088    
089      // The set of attributes for this search request.
090      private final List<String> attributes;
091    
092      // The scope for this search request.
093      private final SearchScope scope;
094    
095      // The base DN for this search request.
096      private final String baseDN;
097    
098    
099    
100      /**
101       * Creates a new search request protocol op with the provided information.
102       *
103       * @param  baseDN       The base DN for this search request.
104       * @param  scope        The scope for this search request.
105       * @param  derefPolicy  The policy to use for aliases encountered during the
106       *                      search.
107       * @param  sizeLimit    The maximum number of entries to return for the
108       *                      search, or zero for no limit.
109       * @param  timeLimit    The maximum length of time to spend processing the
110       *                      search, or zero for no limit.
111       * @param  typesOnly    Indicates whether to return only attribute types or
112       *                      both types and values.
113       * @param  filter       The filter for this search request.
114       * @param  attributes   The names of attributes to include in matching
115       *                      entries.
116       */
117      public SearchRequestProtocolOp(final String baseDN, final SearchScope scope,
118                  final DereferencePolicy derefPolicy, final int sizeLimit,
119                  final int timeLimit, final boolean typesOnly, final Filter filter,
120                  final List<String> attributes)
121      {
122        this.scope       = scope;
123        this.derefPolicy = derefPolicy;
124        this.typesOnly   = typesOnly;
125        this.filter      = filter;
126    
127        if (baseDN == null)
128        {
129          this.baseDN = "";
130        }
131        else
132        {
133          this.baseDN = baseDN;
134        }
135    
136        if (sizeLimit > 0)
137        {
138          this.sizeLimit = sizeLimit;
139        }
140        else
141        {
142          this.sizeLimit = 0;
143        }
144    
145        if (timeLimit > 0)
146        {
147          this.timeLimit = timeLimit;
148        }
149        else
150        {
151          this.timeLimit = 0;
152        }
153    
154        if (attributes == null)
155        {
156          this.attributes = Collections.emptyList();
157        }
158        else
159        {
160          this.attributes = Collections.unmodifiableList(attributes);
161        }
162      }
163    
164    
165    
166      /**
167       * Creates a new search request protocol op from the provided search request
168       * object.
169       *
170       * @param  request  The search request object to use to create this protocol
171       *                  op.
172       */
173      public SearchRequestProtocolOp(final SearchRequest request)
174      {
175        baseDN      = request.getBaseDN();
176        scope       = request.getScope();
177        derefPolicy = request.getDereferencePolicy();
178        sizeLimit   = request.getSizeLimit();
179        timeLimit   = request.getTimeLimitSeconds();
180        typesOnly   = request.typesOnly();
181        filter      = request.getFilter();
182        attributes  = request.getAttributeList();
183      }
184    
185    
186    
187      /**
188       * Creates a new search request protocol op read from the provided ASN.1
189       * stream reader.
190       *
191       * @param  reader  The ASN.1 stream reader from which to read the search
192       *                 request protocol op.
193       *
194       * @throws  LDAPException  If a problem occurs while reading or parsing the
195       *                         search request.
196       */
197      SearchRequestProtocolOp(final ASN1StreamReader reader)
198           throws LDAPException
199      {
200        try
201        {
202          reader.beginSequence();
203          baseDN      = reader.readString();
204          scope       = SearchScope.valueOf(reader.readEnumerated());
205          derefPolicy = DereferencePolicy.valueOf(reader.readEnumerated());
206          sizeLimit   = reader.readInteger();
207          timeLimit   = reader.readInteger();
208          typesOnly   = reader.readBoolean();
209          filter      = Filter.readFrom(reader);
210    
211          final ArrayList<String> attrs = new ArrayList<String>(5);
212          final ASN1StreamReaderSequence attrSequence = reader.beginSequence();
213          while (attrSequence.hasMoreElements())
214          {
215            attrs.add(reader.readString());
216          }
217    
218          attributes = Collections.unmodifiableList(attrs);
219        }
220        catch (LDAPException le)
221        {
222          debugException(le);
223          throw le;
224        }
225        catch (Exception e)
226        {
227          debugException(e);
228    
229          throw new LDAPException(ResultCode.DECODING_ERROR,
230               ERR_SEARCH_REQUEST_CANNOT_DECODE.get(getExceptionMessage(e)), e);
231        }
232      }
233    
234    
235    
236      /**
237       * Retrieves the base DN for this search request.
238       *
239       * @return  The base DN for this search request.
240       */
241      public String getBaseDN()
242      {
243        return baseDN;
244      }
245    
246    
247    
248      /**
249       * Retrieves the scope for this search request.
250       *
251       * @return  The scope for this search request.
252       */
253      public SearchScope getScope()
254      {
255        return scope;
256      }
257    
258    
259    
260      /**
261       * Retrieves the policy to use for any aliases encountered during the search.
262       *
263       * @return  The policy to use for any aliases encountered during the search.
264       */
265      public DereferencePolicy getDerefPolicy()
266      {
267        return derefPolicy;
268      }
269    
270    
271    
272      /**
273       * Retrieves the maximum number of entries that the server should return for
274       * the search.
275       *
276       * @return  The maximum number of entries that the server should return for
277       *          the search, or zero if there is no limit.
278       */
279      public int getSizeLimit()
280      {
281        return sizeLimit;
282      }
283    
284    
285    
286      /**
287       * Retrieves the maximum length of time in seconds the server should spend
288       * processing the search.
289       *
290       * @return  The maximum length of time in seconds the server should spend
291       *          processing the search, or zero if there is no limit.
292       */
293      public int getTimeLimit()
294      {
295        return timeLimit;
296      }
297    
298    
299    
300      /**
301       * Indicates whether the server should return only attribute types or both
302       * attribute types and values.
303       *
304       * @return  {@code true} if the server should return only attribute types, or
305       *          {@code false} if both types and values should be returned.
306       */
307      public boolean typesOnly()
308      {
309        return typesOnly;
310      }
311    
312    
313    
314      /**
315       * Retrieves the filter for this search request.
316       *
317       * @return  The filter for this search request.
318       */
319      public Filter getFilter()
320      {
321        return filter;
322      }
323    
324    
325    
326      /**
327       * Retrieves the set of requested attributes for this search request.
328       *
329       * @return  The set of requested attributes for this search request.
330       */
331      public List<String> getAttributes()
332      {
333        return attributes;
334      }
335    
336    
337    
338      /**
339       * {@inheritDoc}
340       */
341      public byte getProtocolOpType()
342      {
343        return LDAPMessage.PROTOCOL_OP_TYPE_SEARCH_REQUEST;
344      }
345    
346    
347    
348      /**
349       * {@inheritDoc}
350       */
351      public ASN1Element encodeProtocolOp()
352      {
353        final ArrayList<ASN1Element> attrElements =
354             new ArrayList<ASN1Element>(attributes.size());
355        for (final String attribute : attributes)
356        {
357          attrElements.add(new ASN1OctetString(attribute));
358        }
359    
360        return new ASN1Sequence(LDAPMessage.PROTOCOL_OP_TYPE_SEARCH_REQUEST,
361             new ASN1OctetString(baseDN),
362             new ASN1Enumerated(scope.intValue()),
363             new ASN1Enumerated(derefPolicy.intValue()),
364             new ASN1Integer(sizeLimit),
365             new ASN1Integer(timeLimit),
366             new ASN1Boolean(typesOnly),
367             filter.encode(),
368             new ASN1Sequence(attrElements));
369      }
370    
371    
372    
373      /**
374       * Decodes the provided ASN.1 element as a search request protocol op.
375       *
376       * @param  element  The ASN.1 element to be decoded.
377       *
378       * @return  The decoded search request protocol op.
379       *
380       * @throws  LDAPException  If the provided ASN.1 element cannot be decoded as
381       *                         a search request protocol op.
382       */
383      public static SearchRequestProtocolOp decodeProtocolOp(
384                                                 final ASN1Element element)
385             throws LDAPException
386      {
387        try
388        {
389          final ASN1Element[] elements =
390               ASN1Sequence.decodeAsSequence(element).elements();
391          final String baseDN =
392               ASN1OctetString.decodeAsOctetString(elements[0]).stringValue();
393          final SearchScope scope = SearchScope.valueOf(
394               ASN1Enumerated.decodeAsEnumerated(elements[1]).intValue());
395          final DereferencePolicy derefPolicy = DereferencePolicy.valueOf(
396               ASN1Enumerated.decodeAsEnumerated(elements[2]).intValue());
397          final int sizeLimit = ASN1Integer.decodeAsInteger(elements[3]).intValue();
398          final int timeLimit = ASN1Integer.decodeAsInteger(elements[4]).intValue();
399          final boolean typesOnly =
400               ASN1Boolean.decodeAsBoolean(elements[5]).booleanValue();
401          final Filter filter = Filter.decode(elements[6]);
402    
403          final ASN1Element[] attrElements =
404               ASN1Sequence.decodeAsSequence(elements[7]).elements();
405          final ArrayList<String> attributes =
406               new ArrayList<String>(attrElements.length);
407          for (final ASN1Element e : attrElements)
408          {
409            attributes.add(ASN1OctetString.decodeAsOctetString(e).stringValue());
410          }
411    
412          return new SearchRequestProtocolOp(baseDN, scope, derefPolicy, sizeLimit,
413               timeLimit, typesOnly, filter, attributes);
414        }
415        catch (final Exception e)
416        {
417          debugException(e);
418          throw new LDAPException(ResultCode.DECODING_ERROR,
419               ERR_SEARCH_REQUEST_CANNOT_DECODE.get(getExceptionMessage(e)),
420               e);
421        }
422      }
423    
424    
425    
426      /**
427       * {@inheritDoc}
428       */
429      public void writeTo(final ASN1Buffer buffer)
430      {
431        final ASN1BufferSequence opSequence =
432             buffer.beginSequence(LDAPMessage.PROTOCOL_OP_TYPE_SEARCH_REQUEST);
433        buffer.addOctetString(baseDN);
434        buffer.addEnumerated(scope.intValue());
435        buffer.addEnumerated(derefPolicy.intValue());
436        buffer.addInteger(sizeLimit);
437        buffer.addInteger(timeLimit);
438        buffer.addBoolean(typesOnly);
439        filter.writeTo(buffer);
440    
441        final ASN1BufferSequence attrSequence = buffer.beginSequence();
442        for (final String s : attributes)
443        {
444          buffer.addOctetString(s);
445        }
446        attrSequence.end();
447        opSequence.end();
448      }
449    
450    
451    
452      /**
453       * Creates a search request from this protocol op.
454       *
455       * @param  controls  The set of controls to include in the search request.
456       *                   It may be empty or {@code null} if no controls should be
457       *                   included.
458       *
459       * @return  The search request that was created.
460       */
461      public SearchRequest toSearchRequest(final Control... controls)
462      {
463        final String[] attrArray = new String[attributes.size()];
464        attributes.toArray(attrArray);
465    
466        return new SearchRequest(null, controls, baseDN, scope, derefPolicy,
467             sizeLimit, timeLimit, typesOnly, filter, attrArray);
468      }
469    
470    
471    
472      /**
473       * Retrieves a string representation of this protocol op.
474       *
475       * @return  A string representation of this protocol op.
476       */
477      @Override()
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      public void toString(final StringBuilder buffer)
491      {
492        buffer.append("SearchRequestProtocolOp(baseDN='");
493        buffer.append(baseDN);
494        buffer.append("', scope='");
495        buffer.append(scope.toString());
496        buffer.append("', derefPolicy='");
497        buffer.append(derefPolicy.toString());
498        buffer.append("', sizeLimit=");
499        buffer.append(sizeLimit);
500        buffer.append(", timeLimit=");
501        buffer.append(timeLimit);
502        buffer.append(", typesOnly=");
503        buffer.append(typesOnly);
504        buffer.append(", filter='");
505        filter.toString(buffer);
506        buffer.append("', attributes={");
507    
508        final Iterator<String> iterator = attributes.iterator();
509        while (iterator.hasNext())
510        {
511          buffer.append(iterator.next());
512          if (iterator.hasNext())
513          {
514            buffer.append(',');
515          }
516        }
517    
518        buffer.append("})");
519      }
520    }