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