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    }