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 */ 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}