001    /*
002     * Copyright 2007-2015 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2008-2015 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.
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    }