001 /*
002 * Copyright 2007-2014 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2008-2014 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.
539 *
540 * @return A string representation of this LDAP result.
541 */
542 @Override()
543 public String toString()
544 {
545 final StringBuilder buffer = new StringBuilder();
546 toString(buffer);
547 return buffer.toString();
548 }
549
550
551
552 /**
553 * Appends a string representation of this LDAP result to the provided buffer.
554 *
555 * @param buffer The buffer to which to append a string representation of
556 * this LDAP result.
557 */
558 public void toString(final StringBuilder buffer)
559 {
560 buffer.append("LDAPResult(resultCode=");
561 buffer.append(resultCode);
562
563 if (messageID >= 0)
564 {
565 buffer.append(", messageID=");
566 buffer.append(messageID);
567 }
568
569 if (protocolOpType != null)
570 {
571 switch (protocolOpType)
572 {
573 case LDAPMessage.PROTOCOL_OP_TYPE_ADD_RESPONSE:
574 buffer.append(", opType='add'");
575 break;
576 case LDAPMessage.PROTOCOL_OP_TYPE_BIND_RESPONSE:
577 buffer.append(", opType='bind'");
578 break;
579 case LDAPMessage.PROTOCOL_OP_TYPE_COMPARE_RESPONSE:
580 buffer.append(", opType='compare'");
581 break;
582 case LDAPMessage.PROTOCOL_OP_TYPE_DELETE_RESPONSE:
583 buffer.append(", opType='delete'");
584 break;
585 case LDAPMessage.PROTOCOL_OP_TYPE_EXTENDED_RESPONSE:
586 buffer.append(", opType='extended'");
587 break;
588 case LDAPMessage.PROTOCOL_OP_TYPE_MODIFY_RESPONSE:
589 buffer.append(", opType='modify'");
590 break;
591 case LDAPMessage.PROTOCOL_OP_TYPE_MODIFY_DN_RESPONSE:
592 buffer.append(", opType='modify DN'");
593 break;
594 case LDAPMessage.PROTOCOL_OP_TYPE_SEARCH_RESULT_DONE:
595 buffer.append(", opType='search'");
596 break;
597 }
598 }
599
600 if (diagnosticMessage != null)
601 {
602 buffer.append(", diagnosticMessage='");
603 buffer.append(diagnosticMessage);
604 buffer.append('\'');
605 }
606
607 if (matchedDN != null)
608 {
609 buffer.append(", matchedDN='");
610 buffer.append(matchedDN);
611 buffer.append('\'');
612 }
613
614 if (referralURLs.length > 0)
615 {
616 buffer.append(", referralURLs={");
617 for (int i=0; i < referralURLs.length; i++)
618 {
619 if (i > 0)
620 {
621 buffer.append(", ");
622 }
623
624 buffer.append('\'');
625 buffer.append(referralURLs[i]);
626 buffer.append('\'');
627 }
628 buffer.append('}');
629 }
630
631 if (responseControls.length > 0)
632 {
633 buffer.append(", responseControls={");
634 for (int i=0; i < responseControls.length; i++)
635 {
636 if (i > 0)
637 {
638 buffer.append(", ");
639 }
640
641 buffer.append(responseControls[i]);
642 }
643 buffer.append('}');
644 }
645
646 buffer.append(')');
647 }
648 }