001/*
002 * Copyright 2009-2017 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2009-2017 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 */
021package com.unboundid.ldap.protocol;
022
023
024
025import java.util.ArrayList;
026import java.util.Collections;
027import java.util.Iterator;
028import java.util.List;
029
030import com.unboundid.asn1.ASN1Boolean;
031import com.unboundid.asn1.ASN1Buffer;
032import com.unboundid.asn1.ASN1BufferSequence;
033import com.unboundid.asn1.ASN1Element;
034import com.unboundid.asn1.ASN1Enumerated;
035import com.unboundid.asn1.ASN1Integer;
036import com.unboundid.asn1.ASN1OctetString;
037import com.unboundid.asn1.ASN1Sequence;
038import com.unboundid.asn1.ASN1StreamReader;
039import com.unboundid.asn1.ASN1StreamReaderSequence;
040import com.unboundid.ldap.sdk.Control;
041import com.unboundid.ldap.sdk.DereferencePolicy;
042import com.unboundid.ldap.sdk.Filter;
043import com.unboundid.ldap.sdk.LDAPException;
044import com.unboundid.ldap.sdk.ResultCode;
045import com.unboundid.ldap.sdk.SearchRequest;
046import com.unboundid.ldap.sdk.SearchScope;
047import com.unboundid.util.NotMutable;
048import com.unboundid.util.InternalUseOnly;
049import com.unboundid.util.ThreadSafety;
050import com.unboundid.util.ThreadSafetyLevel;
051
052import static com.unboundid.ldap.protocol.ProtocolMessages.*;
053import static com.unboundid.util.Debug.*;
054import 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)
064public 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}