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