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