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.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 }