001 /*
002 * Copyright 2007-2016 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2008-2016 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;
022
023
024
025 import java.io.Serializable;
026 import java.util.ArrayList;
027 import java.util.List;
028
029 import com.unboundid.asn1.ASN1Exception;
030 import com.unboundid.asn1.ASN1StreamReader;
031 import com.unboundid.asn1.ASN1StreamReaderSequence;
032 import com.unboundid.ldap.protocol.LDAPMessage;
033 import com.unboundid.ldap.protocol.LDAPResponse;
034 import com.unboundid.util.Extensible;
035 import com.unboundid.util.NotMutable;
036 import com.unboundid.util.ThreadSafety;
037 import com.unboundid.util.ThreadSafetyLevel;
038
039 import static com.unboundid.ldap.sdk.LDAPMessages.*;
040 import static com.unboundid.util.Debug.*;
041 import static com.unboundid.util.StaticUtils.*;
042
043
044
045 /**
046 * This class provides a data structure for holding the elements that are common
047 * to most types of LDAP responses. The elements contained in an LDAP result
048 * include:
049 * <UL>
050 * <LI>Result Code -- An integer value that provides information about the
051 * status of the operation. See the {@link ResultCode} class for
052 * information about a number of result codes defined in LDAP.</LI>
053 * <LI>Diagnostic Message -- An optional string that may provide additional
054 * information about the operation. For example, if the operation failed,
055 * it may include information about the reason for the failure. It will
056 * often (but not always) be absent in the result for successful
057 * operations, and it may be absent in the result for failed
058 * operations.</LI>
059 * <LI>Matched DN -- An optional DN which specifies the entry that most
060 * closely matched the DN of a non-existent entry in the server. For
061 * example, if an operation failed because the target entry did not exist,
062 * then the matched DN field may specify the DN of the closest ancestor
063 * to that entry that does exist in the server.</LI>
064 * <LI>Referral URLs -- An optional set of LDAP URLs which refer to other
065 * directories and/or locations within the DIT in which the operation may
066 * be attempted. If multiple referral URLs are provided, then they should
067 * all be considered equivalent for the purpose of attempting the
068 * operation (e.g., the different URLs may simply refer to different
069 * servers in which the operation could be processed).</LI>
070 * <LI>Response Controls -- An optional set of controls included in the
071 * response from the server. If any controls are included, then they may
072 * provide additional information about the processing that was performed
073 * by the server.</LI>
074 * </UL>
075 * <BR><BR>
076 * Note that even though this class is marked with the @Extensible annotation
077 * type, it should not be directly subclassed by third-party code. Only the
078 * {@link BindResult} and {@link ExtendedResult} subclasses are actually
079 * intended to be extended by third-party code.
080 */
081 @Extensible()
082 @NotMutable()
083 @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
084 public class LDAPResult
085 implements Serializable, LDAPResponse
086 {
087 /**
088 * The BER type for the set of referral URLs.
089 */
090 static final byte TYPE_REFERRAL_URLS = (byte) 0xA3;
091
092
093
094 /**
095 * The serial version UID for this serializable class.
096 */
097 private static final long serialVersionUID = 2215819095653175991L;
098
099
100
101 // The protocol op type for this result, if available.
102 private final Byte protocolOpType;
103
104 // The set of controls from the response.
105 private final Control[] responseControls;
106
107 // The message ID for the LDAP message that is associated with this LDAP
108 // result.
109 private final int messageID;
110
111 // The result code from the response.
112 private final ResultCode resultCode;
113
114 // The diagnostic message from the response, if available.
115 private final String diagnosticMessage;
116
117 // The matched DN from the response, if available.
118 private final String matchedDN;
119
120 // The set of referral URLs from the response, if available.
121 private final String[] referralURLs;
122
123
124
125 /**
126 * Creates a new LDAP result object based on the provided result.
127 *
128 * @param result The LDAP result object to use to initialize this result.
129 */
130 protected LDAPResult(final LDAPResult result)
131 {
132 protocolOpType = result.protocolOpType;
133 messageID = result.messageID;
134 resultCode = result.resultCode;
135 diagnosticMessage = result.diagnosticMessage;
136 matchedDN = result.matchedDN;
137 referralURLs = result.referralURLs;
138 responseControls = result.responseControls;
139 }
140
141
142
143 /**
144 * Creates a new LDAP result object with the provided message ID and result
145 * code, and no other information.
146 *
147 * @param messageID The message ID for the LDAP message that is associated
148 * with this LDAP result.
149 * @param resultCode The result code from the response.
150 */
151 public LDAPResult(final int messageID, final ResultCode resultCode)
152 {
153 this(null, messageID, resultCode, null, null, NO_STRINGS, NO_CONTROLS);
154 }
155
156
157
158 /**
159 * Creates a new LDAP result object with the provided information.
160 *
161 * @param messageID The message ID for the LDAP message that is
162 * associated with this LDAP result.
163 * @param resultCode The result code from the response.
164 * @param diagnosticMessage The diagnostic message from the response, if
165 * available.
166 * @param matchedDN The matched DN from the response, if available.
167 * @param referralURLs The set of referral URLs from the response, if
168 * available.
169 * @param responseControls The set of controls from the response, if
170 * available.
171 */
172 public LDAPResult(final int messageID, final ResultCode resultCode,
173 final String diagnosticMessage, final String matchedDN,
174 final String[] referralURLs,
175 final Control[] responseControls)
176 {
177 this(null, messageID, resultCode, diagnosticMessage, matchedDN,
178 referralURLs, responseControls);
179 }
180
181
182
183 /**
184 * Creates a new LDAP result object with the provided information.
185 *
186 * @param messageID The message ID for the LDAP message that is
187 * associated with this LDAP result.
188 * @param resultCode The result code from the response.
189 * @param diagnosticMessage The diagnostic message from the response, if
190 * available.
191 * @param matchedDN The matched DN from the response, if available.
192 * @param referralURLs The set of referral URLs from the response, if
193 * available.
194 * @param responseControls The set of controls from the response, if
195 * available.
196 */
197 public LDAPResult(final int messageID, final ResultCode resultCode,
198 final String diagnosticMessage, final String matchedDN,
199 final List<String> referralURLs,
200 final List<Control> responseControls)
201 {
202 this(null, messageID, resultCode, diagnosticMessage, matchedDN,
203 referralURLs, responseControls);
204 }
205
206
207
208 /**
209 * Creates a new LDAP result object with the provided information.
210 *
211 * @param protocolOpType The protocol op type for this result, if
212 * available.
213 * @param messageID The message ID for the LDAP message that is
214 * associated with this LDAP result.
215 * @param resultCode The result code from the response.
216 * @param diagnosticMessage The diagnostic message from the response, if
217 * available.
218 * @param matchedDN The matched DN from the response, if available.
219 * @param referralURLs The set of referral URLs from the response, if
220 * available.
221 * @param responseControls The set of controls from the response, if
222 * available.
223 */
224 private LDAPResult(final Byte protocolOpType, final int messageID,
225 final ResultCode resultCode,
226 final String diagnosticMessage, final String matchedDN,
227 final String[] referralURLs,
228 final Control[] responseControls)
229 {
230 this.protocolOpType = protocolOpType;
231 this.messageID = messageID;
232 this.resultCode = resultCode;
233 this.diagnosticMessage = diagnosticMessage;
234 this.matchedDN = matchedDN;
235
236 if (referralURLs == null)
237 {
238 this.referralURLs = NO_STRINGS;
239 }
240 else
241 {
242 this.referralURLs = referralURLs;
243 }
244
245 if (responseControls == null)
246 {
247 this.responseControls = NO_CONTROLS;
248 }
249 else
250 {
251 this.responseControls = responseControls;
252 }
253 }
254
255
256
257 /**
258 * Creates a new LDAP result object with the provided information.
259 *
260 * @param protocolOpType The protocol op type for this result, if
261 * available.
262 * @param messageID The message ID for the LDAP message that is
263 * associated with this LDAP result.
264 * @param resultCode The result code from the response.
265 * @param diagnosticMessage The diagnostic message from the response, if
266 * available.
267 * @param matchedDN The matched DN from the response, if available.
268 * @param referralURLs The set of referral URLs from the response, if
269 * available.
270 * @param responseControls The set of controls from the response, if
271 * available.
272 */
273 private LDAPResult(final Byte protocolOpType, final int messageID,
274 final ResultCode resultCode,
275 final String diagnosticMessage, final String matchedDN,
276 final List<String> referralURLs,
277 final List<Control> responseControls)
278 {
279 this.protocolOpType = protocolOpType;
280 this.messageID = messageID;
281 this.resultCode = resultCode;
282 this.diagnosticMessage = diagnosticMessage;
283 this.matchedDN = matchedDN;
284
285 if ((referralURLs == null) || referralURLs.isEmpty())
286 {
287 this.referralURLs = NO_STRINGS;
288 }
289 else
290 {
291 this.referralURLs = new String[referralURLs.size()];
292 referralURLs.toArray(this.referralURLs);
293 }
294
295 if ((responseControls == null) || responseControls.isEmpty())
296 {
297 this.responseControls = NO_CONTROLS;
298 }
299 else
300 {
301 this.responseControls = new Control[responseControls.size()];
302 responseControls.toArray(this.responseControls);
303 }
304 }
305
306
307
308 /**
309 * Creates a new LDAP result object with the provided message ID and with the
310 * protocol op and controls read from the given ASN.1 stream reader.
311 *
312 * @param messageID The LDAP message ID for the LDAP message that is
313 * associated with this LDAP result.
314 * @param messageSequence The ASN.1 stream reader sequence used in the
315 * course of reading the LDAP message elements.
316 * @param reader The ASN.1 stream reader from which to read the
317 * protocol op and controls.
318 *
319 * @return The decoded LDAP result.
320 *
321 * @throws LDAPException If a problem occurs while reading or decoding data
322 * from the ASN.1 stream reader.
323 */
324 static LDAPResult readLDAPResultFrom(final int messageID,
325 final ASN1StreamReaderSequence messageSequence,
326 final ASN1StreamReader reader)
327 throws LDAPException
328 {
329 try
330 {
331 final ASN1StreamReaderSequence protocolOpSequence =
332 reader.beginSequence();
333 final byte protocolOpType = protocolOpSequence.getType();
334
335 final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
336
337 String matchedDN = reader.readString();
338 if (matchedDN.length() == 0)
339 {
340 matchedDN = null;
341 }
342
343 String diagnosticMessage = reader.readString();
344 if (diagnosticMessage.length() == 0)
345 {
346 diagnosticMessage = null;
347 }
348
349 String[] referralURLs = NO_STRINGS;
350 if (protocolOpSequence.hasMoreElements())
351 {
352 final ArrayList<String> refList = new ArrayList<String>(1);
353 final ASN1StreamReaderSequence refSequence = reader.beginSequence();
354 while (refSequence.hasMoreElements())
355 {
356 refList.add(reader.readString());
357 }
358
359 referralURLs = new String[refList.size()];
360 refList.toArray(referralURLs);
361 }
362
363 Control[] responseControls = NO_CONTROLS;
364 if (messageSequence.hasMoreElements())
365 {
366 final ArrayList<Control> controlList = new ArrayList<Control>(1);
367 final ASN1StreamReaderSequence controlSequence = reader.beginSequence();
368 while (controlSequence.hasMoreElements())
369 {
370 controlList.add(Control.readFrom(reader));
371 }
372
373 responseControls = new Control[controlList.size()];
374 controlList.toArray(responseControls);
375 }
376
377 return new LDAPResult(protocolOpType, messageID, resultCode,
378 diagnosticMessage, matchedDN, referralURLs, responseControls);
379 }
380 catch (LDAPException le)
381 {
382 debugException(le);
383 throw le;
384 }
385 catch (final ASN1Exception ae)
386 {
387 debugException(ae);
388 throw new LDAPException(ResultCode.DECODING_ERROR,
389 ERR_RESULT_CANNOT_DECODE.get(ae.getMessage()), ae);
390 }
391 catch (Exception e)
392 {
393 debugException(e);
394 throw new LDAPException(ResultCode.DECODING_ERROR,
395 ERR_RESULT_CANNOT_DECODE.get(getExceptionMessage(e)), e);
396 }
397 }
398
399
400
401 /**
402 * Retrieves the message ID for the LDAP message with which this LDAP result
403 * is associated.
404 *
405 * @return The message ID for the LDAP message with which this LDAP result
406 * is associated.
407 */
408 public final int getMessageID()
409 {
410 return messageID;
411 }
412
413
414
415 /**
416 * Retrieves the result code from the response.
417 *
418 * @return The result code from the response.
419 */
420 public final ResultCode getResultCode()
421 {
422 return resultCode;
423 }
424
425
426
427 /**
428 * Retrieves the diagnostic message from the response, if available.
429 *
430 * @return The diagnostic message from the response, or {@code null} if none
431 * was provided.
432 */
433 public final String getDiagnosticMessage()
434 {
435 return diagnosticMessage;
436 }
437
438
439
440 /**
441 * Retrieves the matched DN from the response, if available.
442 *
443 * @return The matched DN from the response, or {@code null} if none was
444 * provided.
445 */
446 public final String getMatchedDN()
447 {
448 return matchedDN;
449 }
450
451
452
453 /**
454 * Retrieves the set of referral URLs from the response, if available.
455 *
456 * @return The set of referral URLs from the response. The array returned
457 * may be empty if the response did not include any referral URLs.
458 */
459 public final String[] getReferralURLs()
460 {
461 return referralURLs;
462 }
463
464
465
466 /**
467 * Retrieves the set of controls from the response, if available. Individual
468 * response controls of a specific type may be retrieved and decoded using the
469 * {@code get} method in the response control class.
470 *
471 * @return The set of controls from the response. The array returned may be
472 * empty if the response did not include any controls.
473 */
474 public final Control[] getResponseControls()
475 {
476 return responseControls;
477 }
478
479
480
481 /**
482 * Indicates whether this result contains at least one control.
483 *
484 * @return {@code true} if this result contains at least one control, or
485 * {@code false} if not.
486 */
487 public final boolean hasResponseControl()
488 {
489 return (responseControls.length > 0);
490 }
491
492
493
494 /**
495 * Indicates whether this result contains at least one control with the
496 * specified OID.
497 *
498 * @param oid The object identifier for which to make the determination. It
499 * must not be {@code null}.
500 *
501 * @return {@code true} if this result contains at least one control with
502 * the specified OID, or {@code false} if not.
503 */
504 public final boolean hasResponseControl(final String oid)
505 {
506 for (final Control c : responseControls)
507 {
508 if (c.getOID().equals(oid))
509 {
510 return true;
511 }
512 }
513
514 return false;
515 }
516
517
518
519 /**
520 * Retrieves the response control with the specified OID. If there is more
521 * than one response control with the specified OID, then the first will be
522 * returned.
523 *
524 * @param oid The OID for the response control to retrieve.
525 *
526 * @return The requested response control, or {@code null} if there is no
527 * such response control.
528 */
529 public final Control getResponseControl(final String oid)
530 {
531 for (final Control c : responseControls)
532 {
533 if (c.getOID().equals(oid))
534 {
535 return c;
536 }
537 }
538
539 return null;
540 }
541
542
543
544 /**
545 * Retrieves a string representation of this LDAP result, consisting of
546 * the result code, diagnostic message (if present), matched DN (if present),
547 * and referral URLs (if present).
548 *
549 * @return A string representation of this LDAP result.
550 */
551 public String getResultString()
552 {
553 final StringBuilder buffer = new StringBuilder();
554 buffer.append("result code='");
555 buffer.append(resultCode);
556 buffer.append('\'');
557
558 if ((diagnosticMessage != null) && (diagnosticMessage.length() > 0))
559 {
560 buffer.append(" diagnostic message='");
561 buffer.append(diagnosticMessage);
562 buffer.append('\'');
563 }
564
565 if ((matchedDN != null) && (matchedDN.length() > 0))
566 {
567 buffer.append(" matched DN='");
568 buffer.append(matchedDN);
569 buffer.append('\'');
570 }
571
572 if ((referralURLs != null) && (referralURLs.length > 0))
573 {
574 buffer.append(" referral URLs={");
575
576 for (int i=0; i < referralURLs.length; i++)
577 {
578 if (i > 0)
579 {
580 buffer.append(", ");
581 }
582
583 buffer.append('\'');
584 buffer.append(referralURLs[i]);
585 buffer.append('\'');
586 }
587
588 buffer.append('}');
589 }
590
591 return buffer.toString();
592 }
593
594
595
596 /**
597 * Retrieves a string representation of this LDAP result.
598 *
599 * @return A string representation of this LDAP result.
600 */
601 @Override()
602 public String toString()
603 {
604 final StringBuilder buffer = new StringBuilder();
605 toString(buffer);
606 return buffer.toString();
607 }
608
609
610
611 /**
612 * Appends a string representation of this LDAP result to the provided buffer.
613 *
614 * @param buffer The buffer to which to append a string representation of
615 * this LDAP result.
616 */
617 public void toString(final StringBuilder buffer)
618 {
619 buffer.append("LDAPResult(resultCode=");
620 buffer.append(resultCode);
621
622 if (messageID >= 0)
623 {
624 buffer.append(", messageID=");
625 buffer.append(messageID);
626 }
627
628 if (protocolOpType != null)
629 {
630 switch (protocolOpType)
631 {
632 case LDAPMessage.PROTOCOL_OP_TYPE_ADD_RESPONSE:
633 buffer.append(", opType='add'");
634 break;
635 case LDAPMessage.PROTOCOL_OP_TYPE_BIND_RESPONSE:
636 buffer.append(", opType='bind'");
637 break;
638 case LDAPMessage.PROTOCOL_OP_TYPE_COMPARE_RESPONSE:
639 buffer.append(", opType='compare'");
640 break;
641 case LDAPMessage.PROTOCOL_OP_TYPE_DELETE_RESPONSE:
642 buffer.append(", opType='delete'");
643 break;
644 case LDAPMessage.PROTOCOL_OP_TYPE_EXTENDED_RESPONSE:
645 buffer.append(", opType='extended'");
646 break;
647 case LDAPMessage.PROTOCOL_OP_TYPE_MODIFY_RESPONSE:
648 buffer.append(", opType='modify'");
649 break;
650 case LDAPMessage.PROTOCOL_OP_TYPE_MODIFY_DN_RESPONSE:
651 buffer.append(", opType='modify DN'");
652 break;
653 case LDAPMessage.PROTOCOL_OP_TYPE_SEARCH_RESULT_DONE:
654 buffer.append(", opType='search'");
655 break;
656 }
657 }
658
659 if (diagnosticMessage != null)
660 {
661 buffer.append(", diagnosticMessage='");
662 buffer.append(diagnosticMessage);
663 buffer.append('\'');
664 }
665
666 if (matchedDN != null)
667 {
668 buffer.append(", matchedDN='");
669 buffer.append(matchedDN);
670 buffer.append('\'');
671 }
672
673 if (referralURLs.length > 0)
674 {
675 buffer.append(", referralURLs={");
676 for (int i=0; i < referralURLs.length; i++)
677 {
678 if (i > 0)
679 {
680 buffer.append(", ");
681 }
682
683 buffer.append('\'');
684 buffer.append(referralURLs[i]);
685 buffer.append('\'');
686 }
687 buffer.append('}');
688 }
689
690 if (responseControls.length > 0)
691 {
692 buffer.append(", responseControls={");
693 for (int i=0; i < responseControls.length; i++)
694 {
695 if (i > 0)
696 {
697 buffer.append(", ");
698 }
699
700 buffer.append(responseControls[i]);
701 }
702 buffer.append('}');
703 }
704
705 buffer.append(')');
706 }
707 }