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 proxy values extended
060 * request as used in the UnboundID Directory Proxy Server. It may be used to
061 * obtain all entry DNs and/or 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.8" and the value is encoded as follows:
064 * <PRE>
065 * StreamProxyValuesRequest ::= 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 * backendSets [4] SEQUENCE OF BackendSetConfig,
071 * ... }
072 *
073 * DNSelection ::= SEQUENCE {
074 * scope [0] ENUMERATED {
075 * baseObject (0),
076 * singleLevel (1),
077 * wholeSubtree (2),
078 * subordinateSubtree (3),
079 * ... }
080 * relative [1] BOOLEAN DEFAULT TRUE,
081 * ..... }
082 *
083 * BackendSetConfig ::= SEQUENCE {
084 * backendSetID OCTET STRING,
085 * backendServers SEQUENCE OF SEQUENCE {
086 * host OCTET STRING,
087 * port INTEGER (1 .. 65535) } }
088 * </PRE>
089 */
090 @NotMutable()
091 @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
092 public final class StreamProxyValuesExtendedRequest
093 extends ExtendedRequest
094 {
095 /**
096 * The OID (1.3.6.1.4.1.30221.2.6.8) for the get stream proxy values extended
097 * request.
098 */
099 public static final String STREAM_PROXY_VALUES_REQUEST_OID =
100 "1.3.6.1.4.1.30221.2.6.8";
101
102
103
104 /**
105 * The BER type for the baseDN element of the stream proxy values request
106 * sequence.
107 */
108 private static final byte TYPE_BASE_DN = (byte) 0x80;
109
110
111
112 /**
113 * The BER type for the includeDNs element of the stream proxy values request
114 * sequence.
115 */
116 private static final byte TYPE_INCLUDE_DNS = (byte) 0xA1;
117
118
119
120 /**
121 * The BER type for the attributes element of the stream proxy values request
122 * sequence.
123 */
124 private static final byte TYPE_ATTRIBUTES = (byte) 0xA2;
125
126
127
128 /**
129 * The BER type for the valuesPerResponse element of the stream proxy values
130 * request sequence.
131 */
132 private static final byte TYPE_VALUES_PER_RESPONSE = (byte) 0x83;
133
134
135
136 /**
137 * The BER type for the backendSets element of the stream proxy values request
138 * sequence.
139 */
140 private static final byte TYPE_BACKEND_SETS = (byte) 0xA4;
141
142
143
144 /**
145 * The BER type for the scope element of the DNSelection sequence.
146 */
147 private static final byte TYPE_SCOPE = (byte) 0x80;
148
149
150
151 /**
152 * The BER type for the relative element of the DNSelection sequence.
153 */
154 private static final byte TYPE_RELATIVE = (byte) 0x81;
155
156
157
158 /**
159 * The serial version UID for this serializable class.
160 */
161 private static final long serialVersionUID = 2528621021697410806L;
162
163
164
165 // Indicates whether to return DN values that are relative to the base DN.
166 private final boolean returnRelativeDNs;
167
168 // The maximum number of values to include per response.
169 private final int valuesPerResponse;
170
171 // The list of backend sets defined in the Directory Proxy Server issuing the
172 // request.
173 private final List<StreamProxyValuesBackendSet> backendSets;
174
175 // The list of attribute values to be returned.
176 private final List<String> attributes;
177
178 // The search scope to use if DN values are to be included.
179 private final SearchScope dnScope;
180
181 // The base DN for this stream proxy values request.
182 private final String baseDN;
183
184
185
186 /**
187 * Creates a new stream proxy values extended request with the provided
188 * information.
189 *
190 * @param baseDN The base DN which indicates the portion of the
191 * DIT to target. It must not be {@code null}.
192 * @param dnScope The scope for which to return information about
193 * entry DNs in the specified portion of the DIT.
194 * This may be {@code null} if information about
195 * entry DNs should not be returned.
196 * @param returnRelativeDNs Indicates whether DNs returned should be
197 * relative to the base DN rather than full DNs.
198 * @param attributes The names of the attributes for which to
199 * retrieve the values. This may be {@code null}
200 * or empty if only entry DNs should be retrieved.
201 * @param valuesPerResponse The maximum number of values to include per
202 * response. A value less than or equal to zero
203 * indicates that the server should choose an
204 * appropriate value.
205 * @param backendSets The list of backend sets defined in the
206 * Directory Proxy Server issuing the request. It
207 * must not be {@code null} or empty.
208 * @param controls The set of controls to include in the request.
209 * It may be {@code null} or empty if no controls
210 * should be included in the request.
211 */
212 public StreamProxyValuesExtendedRequest(final String baseDN,
213 final SearchScope dnScope, final boolean returnRelativeDNs,
214 final List<String> attributes, final int valuesPerResponse,
215 final List<StreamProxyValuesBackendSet> backendSets,
216 final Control... controls)
217 {
218 super(STREAM_PROXY_VALUES_REQUEST_OID,
219 encodeValue(baseDN, dnScope, returnRelativeDNs, attributes,
220 valuesPerResponse, backendSets),
221 controls);
222
223 this.baseDN = baseDN;
224 this.dnScope = dnScope;
225 this.returnRelativeDNs = returnRelativeDNs;
226 this.backendSets = Collections.unmodifiableList(backendSets);
227
228 if (attributes == null)
229 {
230 this.attributes = Collections.emptyList();
231 }
232 else
233 {
234 this.attributes = Collections.unmodifiableList(attributes);
235 }
236
237 if (valuesPerResponse < 0)
238 {
239 this.valuesPerResponse = 0;
240 }
241 else
242 {
243 this.valuesPerResponse = valuesPerResponse;
244 }
245 }
246
247
248
249 /**
250 * Creates a new stream proxy values extended request from the provided
251 * generic extended request.
252 *
253 * @param extendedRequest The generic extended request to use to create this
254 * stream proxy values extended request.
255 *
256 * @throws LDAPException If a problem occurs while decoding the request.
257 */
258 public StreamProxyValuesExtendedRequest(
259 final ExtendedRequest extendedRequest)
260 throws LDAPException
261 {
262 super(extendedRequest);
263
264 final ASN1OctetString value = extendedRequest.getValue();
265 if (value == null)
266 {
267 throw new LDAPException(ResultCode.DECODING_ERROR,
268 ERR_STREAM_PROXY_VALUES_REQUEST_NO_VALUE.get());
269 }
270
271 boolean tmpRelative = true;
272 int tmpNumValues = 0;
273 final ArrayList<String> tmpAttrs = new ArrayList<String>();
274 SearchScope tmpScope = null;
275 String tmpBaseDN = null;
276
277 final ArrayList<StreamProxyValuesBackendSet> tmpBackendSets =
278 new ArrayList<StreamProxyValuesBackendSet>();
279
280 try
281 {
282 final ASN1Element[] svElements =
283 ASN1Element.decode(value.getValue()).decodeAsSequence().elements();
284 for (final ASN1Element svElement : svElements)
285 {
286 switch (svElement.getType())
287 {
288 case TYPE_BASE_DN:
289 tmpBaseDN = svElement.decodeAsOctetString().stringValue();
290 break;
291
292 case TYPE_INCLUDE_DNS:
293 final ASN1Element[] idElements =
294 svElement.decodeAsSequence().elements();
295 for (final ASN1Element idElement : idElements)
296 {
297 switch (idElement.getType())
298 {
299 case TYPE_SCOPE:
300 final int scopeValue =
301 idElement.decodeAsEnumerated().intValue();
302 tmpScope = SearchScope.definedValueOf(scopeValue);
303 if (tmpScope == null)
304 {
305 throw new LDAPException(ResultCode.DECODING_ERROR,
306 ERR_STREAM_PROXY_VALUES_REQUEST_INVALID_SCOPE.get(
307 scopeValue));
308 }
309 break;
310 case TYPE_RELATIVE:
311 tmpRelative =
312 idElement.decodeAsBoolean().booleanValue();
313 break;
314 default:
315 throw new LDAPException(ResultCode.DECODING_ERROR,
316 ERR_STREAM_PROXY_VALUES_REQUEST_INVALID_INCLUDE_DNS_TYPE.
317 get(toHex(idElement.getType())));
318 }
319 }
320 break;
321
322 case TYPE_ATTRIBUTES:
323 final ASN1Element[] attrElements =
324 svElement.decodeAsSequence().elements();
325 for (final ASN1Element attrElement : attrElements)
326 {
327 tmpAttrs.add(attrElement.decodeAsOctetString().stringValue());
328 }
329 break;
330
331 case TYPE_VALUES_PER_RESPONSE:
332 tmpNumValues = svElement.decodeAsInteger().intValue();
333 if (tmpNumValues < 0)
334 {
335 tmpNumValues = 0;
336 }
337 break;
338
339 case TYPE_BACKEND_SETS:
340 final ASN1Element[] backendSetElements =
341 svElement.decodeAsSequence().elements();
342 for (final ASN1Element setElement : backendSetElements)
343 {
344 tmpBackendSets.add(
345 StreamProxyValuesBackendSet.decode(setElement));
346 }
347 break;
348
349 default:
350 throw new LDAPException(ResultCode.DECODING_ERROR,
351 ERR_STREAM_PROXY_VALUES_REQUEST_INVALID_SEQUENCE_TYPE.get(
352 toHex(svElement.getType())));
353 }
354 }
355 }
356 catch (LDAPException le)
357 {
358 throw le;
359 }
360 catch (Exception e)
361 {
362 debugException(e);
363 throw new LDAPException(ResultCode.DECODING_ERROR,
364 ERR_STREAM_PROXY_VALUES_REQUEST_CANNOT_DECODE.get(
365 getExceptionMessage(e)), e);
366 }
367
368 if (tmpBaseDN == null)
369 {
370 throw new LDAPException(ResultCode.DECODING_ERROR,
371 ERR_STREAM_PROXY_VALUES_REQUEST_NO_BASE_DN.get());
372 }
373
374 baseDN = tmpBaseDN;
375 dnScope = tmpScope;
376 returnRelativeDNs = tmpRelative;
377 backendSets = Collections.unmodifiableList(tmpBackendSets);
378 attributes = Collections.unmodifiableList(tmpAttrs);
379 valuesPerResponse = tmpNumValues;
380 }
381
382
383
384 /**
385 * Encodes the provided information into a form suitable for use as the value
386 * of this extended request.
387 *
388 * @param baseDN The base DN which indicates the portion of the
389 * DIT to target.
390 * @param scope The scope for which to return information about
391 * entry DNs in the specified portion of the DIT.
392 * This may be {@code null} if information about
393 * entry DNs should not be returned.
394 * @param relativeDNs Indicates whether DNs returned should be
395 * relative to the base DN rather than full DNs.
396 * @param attributes The names of the attributes for which to
397 * retrieve the values. This may be {@code null}
398 * or empty if only entry DNs should be retrieved.
399 * @param valuesPerResponse The maximum number of values to include per
400 * response. A value less than or equal to zero
401 * indicates that the server should choose an
402 * appropriate value.
403 * @param backendSets The list of backend sets defined in the
404 * Directory Proxy Server issuing the request.
405 *
406 * @return The ASN.1 octet string containing the encoded value to use for
407 * this extended request.
408 */
409 private static ASN1OctetString encodeValue(final String baseDN,
410 final SearchScope scope, final boolean relativeDNs,
411 final List<String> attributes, final int valuesPerResponse,
412 final List<StreamProxyValuesBackendSet> backendSets)
413 {
414 ensureNotNull(baseDN, backendSets);
415 ensureFalse(backendSets.isEmpty());
416
417 final ArrayList<ASN1Element> svElements = new ArrayList<ASN1Element>(4);
418 svElements.add(new ASN1OctetString(TYPE_BASE_DN, baseDN));
419
420 if (scope != null)
421 {
422 final ArrayList<ASN1Element> idElements = new ArrayList<ASN1Element>(2);
423 idElements.add(new ASN1Enumerated(TYPE_SCOPE, scope.intValue()));
424
425 if (! relativeDNs)
426 {
427 idElements.add(new ASN1Boolean(TYPE_RELATIVE, relativeDNs));
428 }
429
430 svElements.add(new ASN1Sequence(TYPE_INCLUDE_DNS, idElements));
431 }
432
433 if ((attributes != null) && (! attributes.isEmpty()))
434 {
435 final ArrayList<ASN1Element> attrElements =
436 new ArrayList<ASN1Element>(attributes.size());
437 for (final String s : attributes)
438 {
439 attrElements.add(new ASN1OctetString(s));
440 }
441 svElements.add(new ASN1Sequence(TYPE_ATTRIBUTES, attrElements));
442 }
443
444 if (valuesPerResponse > 0)
445 {
446 svElements.add(new ASN1Integer(TYPE_VALUES_PER_RESPONSE,
447 valuesPerResponse));
448 }
449
450 final ASN1Element[] backendSetElements =
451 new ASN1Element[backendSets.size()];
452 for (int i=0; i < backendSetElements.length; i++)
453 {
454 backendSetElements[i] = backendSets.get(i).encode();
455 }
456 svElements.add(new ASN1Sequence(TYPE_BACKEND_SETS, backendSetElements));
457
458 return new ASN1OctetString(new ASN1Sequence(svElements).encode());
459 }
460
461
462
463 /**
464 * Retrieves the base DN for this request.
465 *
466 * @return The base DN for this request.
467 */
468 public String getBaseDN()
469 {
470 return baseDN;
471 }
472
473
474
475 /**
476 * Retrieves the scope for entry DNs to be included in intermediate responses.
477 *
478 * @return The scope for entry DNs to be included in intermediate responses,
479 * or {@code null} if information about entry DNs should not be
480 * returned.
481 */
482 public SearchScope getDNScope()
483 {
484 return dnScope;
485 }
486
487
488
489 /**
490 * Indicates whether entry DN values returned should be relative to the
491 * provided base DN.
492 *
493 * @return {@code true} if entry DN values returned should be relative to the
494 * provided base DN, or {@code false} if they should be complete DNs.
495 */
496 public boolean returnRelativeDNs()
497 {
498 return returnRelativeDNs;
499 }
500
501
502
503 /**
504 * Retrieves the list of names of attributes whose values should be returned
505 * to the client.
506 *
507 * @return The list of names of attributes whose values should be returned to
508 * the client, or an empty list if only information about entry DNs
509 * should be returned.
510 */
511 public List<String> getAttributes()
512 {
513 return attributes;
514 }
515
516
517
518 /**
519 * Retrieves the maximum number of values that should be included in each
520 * stream proxy values intermediate response.
521 *
522 * @return The maximum number of values that should be included in each
523 * stream proxy values intermediate response, or 0 if the server
524 * should choose the appropriate number of values per response.
525 */
526 public int getValuesPerResponse()
527 {
528 return valuesPerResponse;
529 }
530
531
532
533 /**
534 * Retrieves the list of backend sets defined in the Directory Proxy Server
535 * instance issuing the request.
536 *
537 * @return The list of backend sets defined in the Directory Proxy Server
538 * instance issuing the request.
539 */
540 public List<StreamProxyValuesBackendSet> getBackendSets()
541 {
542 return backendSets;
543 }
544
545
546
547 /**
548 * {@inheritDoc}
549 */
550 @Override()
551 public StreamProxyValuesExtendedRequest duplicate()
552 {
553 return duplicate(getControls());
554 }
555
556
557
558 /**
559 * {@inheritDoc}
560 */
561 @Override()
562 public StreamProxyValuesExtendedRequest duplicate(
563 final Control[] controls)
564 {
565 final StreamProxyValuesExtendedRequest r =
566 new StreamProxyValuesExtendedRequest(baseDN, dnScope,
567 returnRelativeDNs, attributes, valuesPerResponse, backendSets,
568 controls);
569 r.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
570 return r;
571 }
572
573
574
575 /**
576 * {@inheritDoc}
577 */
578 @Override()
579 public String getExtendedRequestName()
580 {
581 return INFO_EXTENDED_REQUEST_NAME_STREAM_PROXY_VALUES.get();
582 }
583
584
585
586 /**
587 * {@inheritDoc}
588 */
589 @Override()
590 public void toString(final StringBuilder buffer)
591 {
592 buffer.append("StreamProxyValuesExtendedRequest(baseDN='");
593 buffer.append(baseDN);
594 buffer.append('\'');
595
596 if (dnScope != null)
597 {
598 buffer.append(", scope='");
599 buffer.append(dnScope.getName());
600 buffer.append("', returnRelativeDNs=");
601 buffer.append(returnRelativeDNs);
602 }
603
604 buffer.append(", attributes={");
605 if (! attributes.isEmpty())
606 {
607 final Iterator<String> iterator = attributes.iterator();
608 while (iterator.hasNext())
609 {
610 buffer.append('\'');
611 buffer.append(iterator.next());
612 buffer.append('\'');
613
614 if (iterator.hasNext())
615 {
616 buffer.append(", ");
617 }
618 }
619 }
620 buffer.append('}');
621
622 if (valuesPerResponse > 0)
623 {
624 buffer.append(", valuesPerResponse=");
625 buffer.append(valuesPerResponse);
626 }
627
628 buffer.append(", backendSets={");
629 final Iterator<StreamProxyValuesBackendSet> setIterator =
630 backendSets.iterator();
631 while (setIterator.hasNext())
632 {
633 setIterator.next().toString(buffer);
634 if (setIterator.hasNext())
635 {
636 buffer.append(", ");
637 }
638 }
639 buffer.append('}');
640
641 final Control[] controls = getControls();
642 if (controls.length > 0)
643 {
644 buffer.append(", controls={");
645 for (int i=0; i < controls.length; i++)
646 {
647 if (i > 0)
648 {
649 buffer.append(", ");
650 }
651
652 buffer.append(controls[i]);
653 }
654 buffer.append('}');
655 }
656
657 buffer.append(')');
658 }
659 }