001 /* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2006-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.dsml.protocol; 028 import org.opends.messages.Message; 029 030 031 032 import java.io.IOException; 033 import java.util.ArrayList; 034 import java.util.LinkedHashSet; 035 import java.util.LinkedList; 036 import java.util.List; 037 import javax.xml.bind.JAXBElement; 038 import org.opends.server.protocols.asn1.ASN1Exception; 039 import org.opends.server.protocols.asn1.ASN1OctetString; 040 import org.opends.server.protocols.ldap.LDAPAttribute; 041 import org.opends.server.protocols.ldap.LDAPConstants; 042 import org.opends.server.protocols.ldap.LDAPFilter; 043 import org.opends.server.protocols.ldap.LDAPMessage; 044 import org.opends.server.protocols.ldap.SearchRequestProtocolOp; 045 import org.opends.server.protocols.ldap.SearchResultEntryProtocolOp; 046 import org.opends.server.protocols.ldap.SearchResultReferenceProtocolOp; 047 import org.opends.server.protocols.ldap.SearchResultDoneProtocolOp; 048 import org.opends.server.tools.LDAPConnection; 049 import org.opends.server.types.ByteString; 050 import org.opends.server.types.DereferencePolicy; 051 import org.opends.server.types.LDAPException; 052 import org.opends.server.types.RawFilter; 053 import org.opends.server.types.SearchScope; 054 055 056 057 /** 058 * This class provides the functionality for the performing an 059 * LDAP SEARCH operation based on the specified DSML request. 060 */ 061 public class DSMLSearchOperation 062 { 063 private LDAPConnection connection; 064 065 /** 066 * Create the instance with the specified connection. 067 * 068 * @param connection The LDAP connection to send the request on. 069 */ 070 071 public DSMLSearchOperation(LDAPConnection connection) 072 { 073 this.connection = connection; 074 } 075 076 /** 077 * Returns a new AND search filter with the provided filter components. 078 * 079 * @param filterSet The filter components for this filter 080 * 081 * @return a new AND search filter with the provided filter components. 082 * 083 * @throws LDAPException an LDAPException is thrown if the creation of a 084 * filter component fails. 085 */ 086 private static LDAPFilter createANDFilter(FilterSet filterSet) 087 throws LDAPException { 088 List<JAXBElement<?>> list = filterSet.getFilterGroup(); 089 ArrayList<RawFilter> filters = new ArrayList<RawFilter>(list.size()); 090 091 for(JAXBElement<?> filter : list) { 092 filters.add(createFilter(filter)); 093 } 094 return LDAPFilter.createANDFilter(filters); 095 } 096 097 /** 098 * Returns a new Approximate search filter with the provided information. 099 * 100 * @param ava the attribute value assertion for this approximate filter. 101 * 102 * @return a new Approximate search filter with the provided information. 103 */ 104 private static LDAPFilter createApproximateFilter(AttributeValueAssertion ava) 105 { 106 return LDAPFilter.createApproximateFilter(ava.getName(), 107 new ASN1OctetString(ava.getValue())); 108 } 109 110 /** 111 * Returns a new Equality search filter with the provided information. 112 * 113 * @param ava the attribute value assertion for this Equality filter. 114 * 115 * @return a new Equality search filter with the provided information. 116 */ 117 private static LDAPFilter createEqualityFilter(AttributeValueAssertion ava) { 118 return LDAPFilter.createEqualityFilter(ava.getName(), 119 new ASN1OctetString(ava.getValue())); 120 } 121 122 /** 123 * Returns a new Extensible search filter with the provided information. 124 * 125 * @param mra the matching rule assertion for this Extensible filter. 126 * 127 * @return a new Extensible search filter with the provided information. 128 */ 129 private static LDAPFilter createExtensibleFilter(MatchingRuleAssertion mra) { 130 return LDAPFilter.createExtensibleFilter(mra.getMatchingRule(), 131 mra.getName(), 132 new ASN1OctetString(mra.getValue()), 133 mra.isDnAttributes()); 134 } 135 136 /** 137 * Returns a new GreaterOrEqual search filter with the provided information. 138 * 139 * @param ava the attribute value assertion for this GreaterOrEqual filter. 140 * 141 * @return a new GreaterOrEqual search filter with the provided information. 142 */ 143 private static LDAPFilter createGreaterOrEqualFilter( 144 AttributeValueAssertion ava) { 145 return LDAPFilter.createGreaterOrEqualFilter(ava.getName(), 146 new ASN1OctetString(ava.getValue())); 147 } 148 149 /** 150 * Returns a new LessOrEqual search filter with the provided information. 151 * 152 * @param ava the attribute value assertion for this LessOrEqual filter. 153 * 154 * @return a new LessOrEqual search filter with the provided information. 155 */ 156 private static LDAPFilter createLessOrEqualFilter( 157 AttributeValueAssertion ava) { 158 return LDAPFilter.createLessOrEqualFilter(ava.getName(), 159 new ASN1OctetString(ava.getValue())); 160 } 161 162 /** 163 * Returns a new NOT search filter with the provided information. 164 * 165 * @param filter the filter for this NOT filter. 166 * 167 * @return a new NOT search filter with the provided information. 168 * 169 * @throws LDAPException an LDAPException is thrown if the creation of the 170 * provided filter fails. 171 */ 172 private static LDAPFilter createNOTFilter(Filter filter) 173 throws LDAPException { 174 return LDAPFilter.createNOTFilter(createFilter(filter)); 175 } 176 177 /** 178 * Returns a new OR search filter with the provided filter components. 179 * 180 * @param filterSet The filter components for this filter 181 * 182 * @return a new OR search filter with the provided filter components. 183 * 184 * @throws LDAPException an LDAPException is thrown if the creation of a 185 * filter component fails. 186 */ 187 private static LDAPFilter createORFilter(FilterSet filterSet) 188 throws LDAPException { 189 List<JAXBElement<?>> list = filterSet.getFilterGroup(); 190 ArrayList<RawFilter> filters = new ArrayList<RawFilter>(list.size()); 191 192 for(JAXBElement<?> filter : list) { 193 filters.add(createFilter(filter)); 194 } 195 return LDAPFilter.createORFilter(filters); 196 } 197 198 /** 199 * Returns a new Present search filter with the provided information. 200 * 201 * @param ad the attribute description for this Present filter. 202 * 203 * @returna new Present search filter with the provided information. 204 * 205 * @throws LDAPException an LDAPException is thrown if the ASN.1 element 206 * provided by the attribute description cannot be 207 * decoded as a raw search filter. 208 */ 209 private static LDAPFilter createPresentFilter(AttributeDescription ad) 210 throws LDAPException { 211 return LDAPFilter.decode( 212 new StringBuilder(ad.getName()).append("=*").toString()); 213 } 214 215 /** 216 * Returns a new Substring search filter with the provided information. 217 * 218 * @param sf the substring filter for this Substring filter. 219 * 220 * @return a new Substring search filter with the provided information. 221 */ 222 private static LDAPFilter createSubstringFilter(SubstringFilter sf) { 223 List<String> anys = sf.getAny(); 224 ArrayList<ByteString> subAnyElements = 225 new ArrayList<ByteString>(anys.size()); 226 227 for(String s : anys) { 228 subAnyElements.add(new ASN1OctetString(s)); 229 } 230 return LDAPFilter.createSubstringFilter(sf.getName(), 231 new ASN1OctetString(sf.getInitial()), 232 subAnyElements, 233 new ASN1OctetString(sf.getFinal())); 234 } 235 236 /** 237 * Returns a new LDAPFilter according to the tag name of the provided element 238 * that can be "and", "or", "not", "equalityMatch", "substrings", 239 * "greaterOrEqual", "lessOrEqual", "present", "approxMatch", 240 * "extensibleMatch". 241 * 242 * @param xmlElement a JAXBElement that contains the name of the filter to 243 * create and the associated argument. 244 * 245 * @return a new LDAPFilter according to the tag name of the provided element. 246 * 247 * @throws LDAPException an LDAPException is thrown if the creation of the 248 * targeted filter fails. 249 */ 250 private static LDAPFilter createFilter(JAXBElement<?> xmlElement) 251 throws LDAPException { 252 LDAPFilter result = null; 253 254 String filterName = xmlElement.getName().getLocalPart(); 255 256 if ( "and".equals(filterName) ) { 257 // <xsd:element name="and" type="FilterSet"/> 258 result = createANDFilter((FilterSet)xmlElement.getValue()); 259 } 260 else if ( "or".equals(filterName) ) { 261 // <xsd:element name="or" type="FilterSet"/> 262 result = createORFilter((FilterSet)xmlElement.getValue()); 263 } 264 else if ( "not".equals(filterName) ) { 265 // <xsd:element name="not" type="Filter"/> 266 result = createNOTFilter((Filter)xmlElement.getValue()); 267 } 268 else if ( "equalityMatch".equals(filterName) ) { 269 // <xsd:element name="equalityMatch" type="AttributeValueAssertion"/> 270 result = createEqualityFilter((AttributeValueAssertion) 271 xmlElement.getValue()); 272 } 273 else if ( "substrings".equals(filterName) ) { 274 // <xsd:element name="substrings" type="SubstringFilter"/> 275 result = createSubstringFilter((SubstringFilter)xmlElement.getValue()); 276 } 277 else if ( "greaterOrEqual".equals(filterName) ) { 278 // <xsd:element name="greaterOrEqual" type="AttributeValueAssertion"/> 279 result = createGreaterOrEqualFilter((AttributeValueAssertion) 280 xmlElement.getValue()); 281 } 282 else if ( "lessOrEqual".equals(filterName) ) { 283 // <xsd:element name="lessOrEqual" type="AttributeValueAssertion"/> 284 result = createLessOrEqualFilter((AttributeValueAssertion) 285 xmlElement.getValue()); 286 } 287 else if ( "present".equals(filterName) ) { 288 // <xsd:element name="present" type="AttributeDescription"/> 289 result = createPresentFilter((AttributeDescription)xmlElement.getValue()); 290 } 291 else if ( "approxMatch".equals(filterName) ) { 292 // <xsd:element name="approxMatch" type="AttributeValueAssertion"/> 293 result = createApproximateFilter((AttributeValueAssertion) 294 xmlElement.getValue()); 295 } 296 else if ( "extensibleMatch".equals(filterName) ) { 297 // <xsd:element name="extensibleMatch" type="MatchingRuleAssertion"/> 298 result = createExtensibleFilter((MatchingRuleAssertion) 299 xmlElement.getValue()); 300 } 301 return result; 302 } 303 304 /** 305 * Returns a new LDAPFilter according to the filter assigned to the provided 306 * filter. 307 * 308 * @param filter a filter that contains the object filter to create. 309 * 310 * @return a new LDAPFilter according to the filter assigned to the provided 311 * filter. 312 * 313 * @throws LDAPException an LDAPException is thrown if the creation of the 314 * targeted filter fails. 315 */ 316 private static LDAPFilter createFilter(Filter filter) 317 throws LDAPException { 318 319 LDAPFilter result = null; 320 321 if ( filter.getAnd() != null ) { 322 result = createANDFilter(filter.getAnd()); 323 } 324 else if ( filter.getApproxMatch() != null ) { 325 result = createApproximateFilter(filter.getApproxMatch()); 326 } 327 else if ( filter.getEqualityMatch() != null ) { 328 result = createEqualityFilter(filter.getEqualityMatch()); 329 } 330 else if ( filter.getExtensibleMatch() != null ) { 331 result = createExtensibleFilter(filter.getExtensibleMatch()); 332 } 333 else if ( filter.getGreaterOrEqual() != null ) { 334 result = createGreaterOrEqualFilter(filter.getGreaterOrEqual()); 335 } 336 else if ( filter.getLessOrEqual() != null ) { 337 result = createLessOrEqualFilter(filter.getLessOrEqual()); 338 } 339 else if ( filter.getNot() != null ) { 340 result = createNOTFilter(filter.getNot()); 341 } 342 else if ( filter.getOr() != null ) { 343 result = createORFilter(filter.getOr()); 344 } 345 else if ( filter.getPresent() != null ) { 346 result = createPresentFilter(filter.getPresent()); 347 } 348 else if ( filter.getSubstrings() != null ) { 349 result = createSubstringFilter(filter.getSubstrings()); 350 } 351 return result; 352 } 353 354 /** 355 * Perform the LDAP SEARCH operation and send the result back to the 356 * client. 357 * 358 * @param objFactory The object factory for this operation. 359 * @param searchRequest The search request for this operation. 360 * 361 * @return The result of the add operation. 362 * 363 * @throws IOException If an I/O problem occurs. 364 * 365 * @throws LDAPException If an error occurs while interacting with an LDAP 366 * element. 367 */ 368 public SearchResponse doSearch(ObjectFactory objFactory, 369 SearchRequest searchRequest) 370 throws IOException, LDAPException 371 { 372 SearchResponse searchResponse = objFactory.createSearchResponse(); 373 searchResponse.setRequestID(searchRequest.getRequestID()); 374 375 LDAPFilter filter = createFilter(searchRequest.getFilter()); 376 377 DereferencePolicy derefPolicy = DereferencePolicy.NEVER_DEREF_ALIASES; 378 String derefStr = searchRequest.getDerefAliases().toLowerCase(); 379 if (derefStr.equals("derefinsearching")) 380 { 381 derefPolicy = DereferencePolicy.DEREF_IN_SEARCHING; 382 } 383 else if (derefStr.equals("dereffindingbaseobj")) 384 { 385 derefPolicy = DereferencePolicy.DEREF_FINDING_BASE_OBJECT; 386 } 387 else if (derefStr.equals("derefalways")) 388 { 389 derefPolicy = DereferencePolicy.DEREF_ALWAYS; 390 } 391 392 SearchScope scope = SearchScope.WHOLE_SUBTREE; 393 String scopeStr = searchRequest.getScope().toLowerCase(); 394 if(scopeStr.equals("singlelevel") || scopeStr.equals("one")) 395 { 396 scope = SearchScope.SINGLE_LEVEL; 397 } else if(scopeStr.equals("baseobject") || scopeStr.equals("base")) 398 { 399 scope = SearchScope.BASE_OBJECT; 400 } 401 402 LinkedHashSet<String> attributes = new LinkedHashSet<String>(); 403 // Get the list of attributes. 404 AttributeDescriptions attrDescriptions = searchRequest.getAttributes(); 405 if(attrDescriptions != null) 406 { 407 List<AttributeDescription> attrDesc = attrDescriptions.getAttribute(); 408 for(AttributeDescription desc : attrDesc) 409 { 410 attributes.add(desc.getName()); 411 } 412 } 413 414 SearchRequestProtocolOp protocolOp = new SearchRequestProtocolOp( 415 new ASN1OctetString(searchRequest.getDn()), 416 scope, derefPolicy, 417 (int) searchRequest.getSizeLimit(), 418 (int) searchRequest.getTimeLimit(), 419 searchRequest.isTypesOnly(), filter, attributes); 420 try 421 { 422 LDAPMessage msg = new LDAPMessage(DSMLServlet.nextMessageID(), 423 protocolOp); 424 connection.getLDAPWriter().writeMessage(msg); 425 426 byte opType; 427 do 428 { 429 int resultCode = 0; 430 Message errorMessage = null; 431 LDAPMessage responseMessage = 432 connection.getLDAPReader().readMessage(); 433 434 opType = responseMessage.getProtocolOpType(); 435 switch(opType) 436 { 437 case LDAPConstants.OP_TYPE_SEARCH_RESULT_ENTRY: 438 SearchResultEntryProtocolOp searchEntryOp = 439 responseMessage.getSearchResultEntryProtocolOp(); 440 441 SearchResultEntry entry = objFactory.createSearchResultEntry(); 442 java.util.List<DsmlAttr> attrList = entry.getAttr(); 443 444 LinkedList<LDAPAttribute> attrs = searchEntryOp.getAttributes(); 445 446 for(LDAPAttribute attr : attrs) 447 { 448 String nm = attr.getAttributeType(); 449 DsmlAttr dsmlAttr = objFactory.createDsmlAttr(); 450 451 dsmlAttr.setName(nm); 452 List<String> dsmlAttrVal = dsmlAttr.getValue(); 453 ArrayList<ASN1OctetString> vals = attr.getValues(); 454 for(ASN1OctetString val : vals) 455 { 456 dsmlAttrVal.add(val.toString()); 457 } 458 attrList.add(dsmlAttr); 459 } 460 461 entry.setDn(searchEntryOp.getDN().toString()); 462 searchResponse.getSearchResultEntry().add(entry); 463 break; 464 465 case LDAPConstants.OP_TYPE_SEARCH_RESULT_REFERENCE: 466 SearchResultReferenceProtocolOp searchRefOp = 467 responseMessage.getSearchResultReferenceProtocolOp(); 468 break; 469 470 case LDAPConstants.OP_TYPE_SEARCH_RESULT_DONE: 471 SearchResultDoneProtocolOp searchOp = 472 responseMessage.getSearchResultDoneProtocolOp(); 473 resultCode = searchOp.getResultCode(); 474 errorMessage = searchOp.getErrorMessage(); 475 LDAPResult result = objFactory.createLDAPResult(); 476 ResultCode code = objFactory.createResultCode(); 477 code.setCode(resultCode); 478 result.setResultCode(code); 479 result.setErrorMessage( 480 errorMessage != null ? errorMessage.toString() : null); 481 if(searchOp.getMatchedDN() != null) 482 { 483 result.setMatchedDN(searchOp.getMatchedDN().toString()); 484 } 485 searchResponse.setSearchResultDone(result); 486 break; 487 default: 488 throw new RuntimeException("Invalid protocol operation:" + opType); 489 } 490 } while(opType != LDAPConstants.OP_TYPE_SEARCH_RESULT_DONE); 491 492 } catch(ASN1Exception ae) 493 { 494 ae.printStackTrace(); 495 throw new IOException(ae.getMessage()); 496 } 497 498 return searchResponse; 499 } 500 } 501