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 */
021package com.unboundid.ldap.sdk;
022
023
024
025import java.io.Serializable;
026import java.util.ArrayList;
027import java.util.List;
028
029import com.unboundid.asn1.ASN1Exception;
030import com.unboundid.asn1.ASN1StreamReader;
031import com.unboundid.asn1.ASN1StreamReaderSequence;
032import com.unboundid.ldap.protocol.LDAPMessage;
033import com.unboundid.ldap.protocol.LDAPResponse;
034import com.unboundid.util.Extensible;
035import com.unboundid.util.NotMutable;
036import com.unboundid.util.ThreadSafety;
037import com.unboundid.util.ThreadSafetyLevel;
038
039import static com.unboundid.ldap.sdk.LDAPMessages.*;
040import static com.unboundid.util.Debug.*;
041import 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)
084public 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.
546   *
547   * @return  A string representation of this LDAP result.
548   */
549  @Override()
550  public String toString()
551  {
552    final StringBuilder buffer = new StringBuilder();
553    toString(buffer);
554    return buffer.toString();
555  }
556
557
558
559  /**
560   * Appends a string representation of this LDAP result to the provided buffer.
561   *
562   * @param  buffer  The buffer to which to append a string representation of
563   *                 this LDAP result.
564   */
565  public void toString(final StringBuilder buffer)
566  {
567    buffer.append("LDAPResult(resultCode=");
568    buffer.append(resultCode);
569
570    if (messageID >= 0)
571    {
572      buffer.append(", messageID=");
573      buffer.append(messageID);
574    }
575
576    if (protocolOpType != null)
577    {
578      switch (protocolOpType)
579      {
580        case LDAPMessage.PROTOCOL_OP_TYPE_ADD_RESPONSE:
581          buffer.append(", opType='add'");
582          break;
583        case LDAPMessage.PROTOCOL_OP_TYPE_BIND_RESPONSE:
584          buffer.append(", opType='bind'");
585          break;
586        case LDAPMessage.PROTOCOL_OP_TYPE_COMPARE_RESPONSE:
587          buffer.append(", opType='compare'");
588          break;
589        case LDAPMessage.PROTOCOL_OP_TYPE_DELETE_RESPONSE:
590          buffer.append(", opType='delete'");
591          break;
592        case LDAPMessage.PROTOCOL_OP_TYPE_EXTENDED_RESPONSE:
593          buffer.append(", opType='extended'");
594          break;
595        case LDAPMessage.PROTOCOL_OP_TYPE_MODIFY_RESPONSE:
596          buffer.append(", opType='modify'");
597          break;
598        case LDAPMessage.PROTOCOL_OP_TYPE_MODIFY_DN_RESPONSE:
599          buffer.append(", opType='modify DN'");
600          break;
601        case LDAPMessage.PROTOCOL_OP_TYPE_SEARCH_RESULT_DONE:
602          buffer.append(", opType='search'");
603          break;
604      }
605    }
606
607    if (diagnosticMessage != null)
608    {
609      buffer.append(", diagnosticMessage='");
610      buffer.append(diagnosticMessage);
611      buffer.append('\'');
612    }
613
614    if (matchedDN != null)
615    {
616      buffer.append(", matchedDN='");
617      buffer.append(matchedDN);
618      buffer.append('\'');
619    }
620
621    if (referralURLs.length > 0)
622    {
623      buffer.append(", referralURLs={");
624      for (int i=0; i < referralURLs.length; i++)
625      {
626        if (i > 0)
627        {
628          buffer.append(", ");
629        }
630
631        buffer.append('\'');
632        buffer.append(referralURLs[i]);
633        buffer.append('\'');
634      }
635      buffer.append('}');
636    }
637
638    if (responseControls.length > 0)
639    {
640      buffer.append(", responseControls={");
641      for (int i=0; i < responseControls.length; i++)
642      {
643        if (i > 0)
644        {
645          buffer.append(", ");
646        }
647
648        buffer.append(responseControls[i]);
649      }
650      buffer.append('}');
651    }
652
653    buffer.append(')');
654  }
655}