001 /*
002 * Copyright 2009-2016 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2009-2016 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 }