001 /*
002 * Copyright 2009-2015 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 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.sdk.unboundidds.extensions;
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.ASN1Element;
032 import com.unboundid.asn1.ASN1Enumerated;
033 import com.unboundid.asn1.ASN1OctetString;
034 import com.unboundid.asn1.ASN1Sequence;
035 import com.unboundid.asn1.ASN1Integer;
036 import com.unboundid.ldap.sdk.Control;
037 import com.unboundid.ldap.sdk.ExtendedRequest;
038 import com.unboundid.ldap.sdk.LDAPException;
039 import com.unboundid.ldap.sdk.ResultCode;
040 import com.unboundid.ldap.sdk.SearchScope;
041 import com.unboundid.util.NotMutable;
042 import com.unboundid.util.ThreadSafety;
043 import com.unboundid.util.ThreadSafetyLevel;
044
045 import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*;
046 import static com.unboundid.util.Debug.*;
047 import static com.unboundid.util.StaticUtils.*;
048 import static com.unboundid.util.Validator.*;
049
050
051
052 /**
053 * <BLOCKQUOTE>
054 * <B>NOTE:</B> This class is part of the Commercial Edition of the UnboundID
055 * LDAP SDK for Java. It is not available for use in applications that
056 * include only the Standard Edition of the LDAP SDK, and is not supported for
057 * use in conjunction with non-UnboundID products.
058 * </BLOCKQUOTE>
059 * This class provides an implementation of the stream directory values extended
060 * request as used in the UnboundID Directory Server. It may be used to obtain
061 * all entry DNs and/or all all values for one or more attributes for a
062 * specified portion of the DIT. This extended request has an OID of
063 * "1.3.6.1.4.1.30221.2.6.6" and the value is encoded as follows:
064 * <PRE>
065 * StreamDirectoryValuesRequest ::= SEQUENCE {
066 * baseDN [0] LDAPDN,
067 * includeDNs [1] DNSelection OPTIONAL,
068 * attributes [2] SEQUENCE OF LDAPString OPTIONAL,
069 * valuesPerResponse [3] INTEGER (1 .. 32767) OPTIONAL,
070 * ... }
071 *
072 * DNSelection ::= SEQUENCE {
073 * scope [0] ENUMERATED {
074 * baseObject (0),
075 * singleLevel (1),
076 * wholeSubtree (2),
077 * subordinateSubtree (3),
078 * ... }
079 * relative [1] BOOLEAN DEFAULT TRUE,
080 * ..... }
081 * </PRE>
082 */
083 @NotMutable()
084 @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
085 public final class StreamDirectoryValuesExtendedRequest
086 extends ExtendedRequest
087 {
088 /**
089 * The OID (1.3.6.1.4.1.30221.2.6.6) for the get stream directory values
090 * extended request.
091 */
092 public static final String STREAM_DIRECTORY_VALUES_REQUEST_OID =
093 "1.3.6.1.4.1.30221.2.6.6";
094
095
096
097 /**
098 * The BER type for the baseDN element of the stream directory values request
099 * sequence.
100 */
101 private static final byte TYPE_BASE_DN = (byte) 0x80;
102
103
104
105 /**
106 * The BER type for the includeDNs element of the stream directory values
107 * request sequence.
108 */
109 private static final byte TYPE_INCLUDE_DNS = (byte) 0xA1;
110
111
112
113 /**
114 * The BER type for the attributes element of the stream directory values
115 * request sequence.
116 */
117 private static final byte TYPE_ATTRIBUTES = (byte) 0xA2;
118
119
120
121 /**
122 * The BER type for the valuesPerResponse element of the stream directory
123 * values request sequence.
124 */
125 private static final byte TYPE_VALUES_PER_RESPONSE = (byte) 0x83;
126
127
128
129 /**
130 * The BER type for the scope element of the DNSelection sequence.
131 */
132 private static final byte TYPE_SCOPE = (byte) 0x80;
133
134
135
136 /**
137 * The BER type for the relative element of the DNSelection sequence.
138 */
139 private static final byte TYPE_RELATIVE = (byte) 0x81;
140
141
142
143 /**
144 * The serial version UID for this serializable class.
145 */
146 private static final long serialVersionUID = -6365315263363449596L;
147
148
149
150 // Indicates whether to return DN values that are relative to the base DN.
151 private final boolean returnRelativeDNs;
152
153 // The maximum number of values to include per response.
154 private final int valuesPerResponse;
155
156 // The list of attribute values to be returned.
157 private final List<String> attributes;
158
159 // The search scope to use if DN values are to be included.
160 private final SearchScope dnScope;
161
162 // The base DN for this stream directory values request.
163 private final String baseDN;
164
165
166
167 /**
168 * Creates a new stream directory values extended request with the provided
169 * information.
170 *
171 * @param baseDN The base DN which indicates the portion of the
172 * DIT to target. It must not be {@code null}.
173 * @param dnScope The scope for which to return information about
174 * entry DNs in the specified portion of the DIT.
175 * This may be {@code null} if information about
176 * entry DNs should not be returned.
177 * @param returnRelativeDNs Indicates whether DNs returned should be
178 * relative to the base DN rather than full DNs.
179 * @param attributes The names of the attributes for which to
180 * retrieve the values. This may be {@code null}
181 * or empty if only entry DNs should be retrieved.
182 * @param valuesPerResponse The maximum number of values to include per
183 * response. A value less than or equal to zero
184 * indicates that the server should choose an
185 * appropriate value.
186 * @param controls The set of controls to include in the request.
187 * It may be {@code null} or empty if no controls
188 * should be included in the request.
189 */
190 public StreamDirectoryValuesExtendedRequest(final String baseDN,
191 final SearchScope dnScope, final boolean returnRelativeDNs,
192 final List<String> attributes, final int valuesPerResponse,
193 final Control... controls)
194 {
195 super(STREAM_DIRECTORY_VALUES_REQUEST_OID,
196 encodeValue(baseDN, dnScope, returnRelativeDNs, attributes,
197 valuesPerResponse),
198 controls);
199
200 this.baseDN = baseDN;
201 this.dnScope = dnScope;
202 this.returnRelativeDNs = returnRelativeDNs;
203
204 if (attributes == null)
205 {
206 this.attributes = Collections.emptyList();
207 }
208 else
209 {
210 this.attributes = Collections.unmodifiableList(attributes);
211 }
212
213 if (valuesPerResponse < 0)
214 {
215 this.valuesPerResponse = 0;
216 }
217 else
218 {
219 this.valuesPerResponse = valuesPerResponse;
220 }
221 }
222
223
224
225 /**
226 * Creates a new stream directory values extended request from the provided
227 * generic extended request.
228 *
229 * @param extendedRequest The generic extended request to use to create this
230 * stream directory values extended request.
231 *
232 * @throws LDAPException If a problem occurs while decoding the request.
233 */
234 public StreamDirectoryValuesExtendedRequest(
235 final ExtendedRequest extendedRequest)
236 throws LDAPException
237 {
238 super(extendedRequest);
239
240 final ASN1OctetString value = extendedRequest.getValue();
241 if (value == null)
242 {
243 throw new LDAPException(ResultCode.DECODING_ERROR,
244 ERR_STREAM_DIRECTORY_VALUES_REQUEST_NO_VALUE.get());
245 }
246
247 boolean tmpRelative = true;
248 int tmpNumValues = 0;
249 final ArrayList<String> tmpAttrs = new ArrayList<String>();
250 SearchScope tmpScope = null;
251 String tmpBaseDN = null;
252
253 try
254 {
255 final ASN1Element[] svElements =
256 ASN1Element.decode(value.getValue()).decodeAsSequence().elements();
257 for (final ASN1Element svElement : svElements)
258 {
259 switch (svElement.getType())
260 {
261 case TYPE_BASE_DN:
262 tmpBaseDN = svElement.decodeAsOctetString().stringValue();
263 break;
264
265 case TYPE_INCLUDE_DNS:
266 final ASN1Element[] idElements =
267 svElement.decodeAsSequence().elements();
268 for (final ASN1Element idElement : idElements)
269 {
270 switch (idElement.getType())
271 {
272 case TYPE_SCOPE:
273 final int scopeValue =
274 idElement.decodeAsEnumerated().intValue();
275 tmpScope = SearchScope.definedValueOf(scopeValue);
276 if (tmpScope == null)
277 {
278 throw new LDAPException(ResultCode.DECODING_ERROR,
279 ERR_STREAM_DIRECTORY_VALUES_REQUEST_INVALID_SCOPE.get(
280 scopeValue));
281 }
282 break;
283 case TYPE_RELATIVE:
284 tmpRelative =
285 idElement.decodeAsBoolean().booleanValue();
286 break;
287 default:
288 throw new LDAPException(ResultCode.DECODING_ERROR,
289 ERR_STREAM_DIRECTORY_VALUES_REQUEST_INVALID_INCLUDE_DNS_TYPE.
290 get(toHex(idElement.getType())));
291 }
292 }
293 break;
294
295 case TYPE_ATTRIBUTES:
296 final ASN1Element[] attrElements =
297 svElement.decodeAsSequence().elements();
298 for (final ASN1Element attrElement : attrElements)
299 {
300 tmpAttrs.add(attrElement.decodeAsOctetString().stringValue());
301 }
302 break;
303
304 case TYPE_VALUES_PER_RESPONSE:
305 tmpNumValues = svElement.decodeAsInteger().intValue();
306 if (tmpNumValues < 0)
307 {
308 tmpNumValues = 0;
309 }
310 break;
311
312 default:
313 throw new LDAPException(ResultCode.DECODING_ERROR,
314 ERR_STREAM_DIRECTORY_VALUES_REQUEST_INVALID_SEQUENCE_TYPE.get(
315 toHex(svElement.getType())));
316 }
317 }
318 }
319 catch (LDAPException le)
320 {
321 throw le;
322 }
323 catch (Exception e)
324 {
325 debugException(e);
326 throw new LDAPException(ResultCode.DECODING_ERROR,
327 ERR_STREAM_DIRECTORY_VALUES_REQUEST_CANNOT_DECODE.get(
328 getExceptionMessage(e)), e);
329 }
330
331 if (tmpBaseDN == null)
332 {
333 throw new LDAPException(ResultCode.DECODING_ERROR,
334 ERR_STREAM_DIRECTORY_VALUES_REQUEST_NO_BASE_DN.get());
335 }
336
337 baseDN = tmpBaseDN;
338 dnScope = tmpScope;
339 returnRelativeDNs = tmpRelative;
340 attributes = Collections.unmodifiableList(tmpAttrs);
341 valuesPerResponse = tmpNumValues;
342 }
343
344
345
346 /**
347 * Encodes the provided information into a form suitable for use as the value
348 * of this extended request.
349 *
350 * @param baseDN The base DN which indicates the portion of the
351 * DIT to target.
352 * @param scope The scope for which to return information about
353 * entry DNs in the specified portion of the DIT.
354 * This may be {@code null} if information about
355 * entry DNs should not be returned.
356 * @param relativeDNs Indicates whether DNs returned should be
357 * relative to the base DN rather than full DNs.
358 * @param attributes The names of the attributes for which to
359 * retrieve the values. This may be {@code null}
360 * or empty if only entry DNs should be retrieved.
361 * @param valuesPerResponse The maximum number of values to include per
362 * response. A value less than or equal to zero
363 * indicates that the server should choose an
364 * appropriate value.
365 *
366 * @return The ASN.1 octet string containing the encoded value to use for
367 * this extended request.
368 */
369 private static ASN1OctetString encodeValue(final String baseDN,
370 final SearchScope scope, final boolean relativeDNs,
371 final List<String> attributes, final int valuesPerResponse)
372 {
373 ensureNotNull(baseDN);
374
375 final ArrayList<ASN1Element> svElements = new ArrayList<ASN1Element>(4);
376 svElements.add(new ASN1OctetString(TYPE_BASE_DN, baseDN));
377
378 if (scope != null)
379 {
380 final ArrayList<ASN1Element> idElements = new ArrayList<ASN1Element>(2);
381 idElements.add(new ASN1Enumerated(TYPE_SCOPE, scope.intValue()));
382
383 if (! relativeDNs)
384 {
385 idElements.add(new ASN1Boolean(TYPE_RELATIVE, relativeDNs));
386 }
387
388 svElements.add(new ASN1Sequence(TYPE_INCLUDE_DNS, idElements));
389 }
390
391 if ((attributes != null) && (! attributes.isEmpty()))
392 {
393 final ArrayList<ASN1Element> attrElements =
394 new ArrayList<ASN1Element>(attributes.size());
395 for (final String s : attributes)
396 {
397 attrElements.add(new ASN1OctetString(s));
398 }
399 svElements.add(new ASN1Sequence(TYPE_ATTRIBUTES, attrElements));
400 }
401
402 if (valuesPerResponse > 0)
403 {
404 svElements.add(new ASN1Integer(TYPE_VALUES_PER_RESPONSE,
405 valuesPerResponse));
406 }
407
408 return new ASN1OctetString(new ASN1Sequence(svElements).encode());
409 }
410
411
412
413 /**
414 * Retrieves the base DN for this request.
415 *
416 * @return The base DN for this request.
417 */
418 public String getBaseDN()
419 {
420 return baseDN;
421 }
422
423
424
425 /**
426 * Retrieves the scope for entry DNs to be included in intermediate responses.
427 *
428 * @return The scope for entry DNs to be included in intermediate responses,
429 * or {@code null} if information about entry DNs should not be
430 * returned.
431 */
432 public SearchScope getDNScope()
433 {
434 return dnScope;
435 }
436
437
438
439 /**
440 * Indicates whether entry DN values returned should be relative to the
441 * provided base DN.
442 *
443 * @return {@code true} if entry DN values returned should be relative to the
444 * provided base DN, or {@code false} if they should be complete DNs.
445 */
446 public boolean returnRelativeDNs()
447 {
448 return returnRelativeDNs;
449 }
450
451
452
453 /**
454 * Retrieves the list of names of attributes whose values should be returned
455 * to the client.
456 *
457 * @return The list of names of attributes whose values should be returned to
458 * the client, or an empty list if only information about entry DNs
459 * should be returned.
460 */
461 public List<String> getAttributes()
462 {
463 return attributes;
464 }
465
466
467
468 /**
469 * Retrieves the maximum number of values that should be included in each
470 * stream directory values intermediate response.
471 *
472 * @return The maximum number of values that should be included in each
473 * stream directory values intermediate response, or 0 if the server
474 * should choose the appropriate number of values per response.
475 */
476 public int getValuesPerResponse()
477 {
478 return valuesPerResponse;
479 }
480
481
482
483 /**
484 * {@inheritDoc}
485 */
486 @Override()
487 public StreamDirectoryValuesExtendedRequest duplicate()
488 {
489 return duplicate(getControls());
490 }
491
492
493
494 /**
495 * {@inheritDoc}
496 */
497 @Override()
498 public StreamDirectoryValuesExtendedRequest duplicate(
499 final Control[] controls)
500 {
501 final StreamDirectoryValuesExtendedRequest r =
502 new StreamDirectoryValuesExtendedRequest(baseDN, dnScope,
503 returnRelativeDNs, attributes, valuesPerResponse, controls);
504 r.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
505 return r;
506 }
507
508
509
510 /**
511 * {@inheritDoc}
512 */
513 @Override()
514 public String getExtendedRequestName()
515 {
516 return INFO_EXTENDED_REQUEST_NAME_STREAM_DIRECTORY_VALUES.get();
517 }
518
519
520
521 /**
522 * {@inheritDoc}
523 */
524 @Override()
525 public void toString(final StringBuilder buffer)
526 {
527 buffer.append("StreamDirectoryValuesExtendedRequest(baseDN='");
528 buffer.append(baseDN);
529 buffer.append('\'');
530
531 if (dnScope != null)
532 {
533 buffer.append(", scope='");
534 buffer.append(dnScope.getName());
535 buffer.append("', returnRelativeDNs=");
536 buffer.append(returnRelativeDNs);
537 }
538
539 buffer.append(", attributes={");
540 if (! attributes.isEmpty())
541 {
542 final Iterator<String> iterator = attributes.iterator();
543 while (iterator.hasNext())
544 {
545 buffer.append('\'');
546 buffer.append(iterator.next());
547 buffer.append('\'');
548
549 if (iterator.hasNext())
550 {
551 buffer.append(", ");
552 }
553 }
554 }
555 buffer.append('}');
556
557 if (valuesPerResponse > 0)
558 {
559 buffer.append(", valuesPerResponse=");
560 buffer.append(valuesPerResponse);
561 }
562
563 final Control[] controls = getControls();
564 if (controls.length > 0)
565 {
566 buffer.append(", controls={");
567 for (int i=0; i < controls.length; i++)
568 {
569 if (i > 0)
570 {
571 buffer.append(", ");
572 }
573
574 buffer.append(controls[i]);
575 }
576 buffer.append('}');
577 }
578
579 buffer.append(')');
580 }
581 }