001    /*
002     * Copyright 2007-2016 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2008-2016 UnboundID Corp.
007     *
008     * This program is free software; you can redistribute it and/or modify
009     * it under the terms of the GNU General Public License (GPLv2 only)
010     * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011     * as published by the Free Software Foundation.
012     *
013     * This program is distributed in the hope that it will be useful,
014     * but WITHOUT ANY WARRANTY; without even the implied warranty of
015     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016     * GNU General Public License for more details.
017     *
018     * You should have received a copy of the GNU General Public License
019     * along with this program; if not, see <http://www.gnu.org/licenses>.
020     */
021    package com.unboundid.ldap.sdk;
022    
023    
024    
025    import java.util.ArrayList;
026    
027    import com.unboundid.asn1.ASN1OctetString;
028    import com.unboundid.asn1.ASN1StreamReader;
029    import com.unboundid.asn1.ASN1StreamReaderSequence;
030    import com.unboundid.util.Extensible;
031    import com.unboundid.util.NotMutable;
032    import com.unboundid.util.ThreadSafety;
033    import com.unboundid.util.ThreadSafetyLevel;
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 information about the result
043     * of processing an extended operation.  It includes all of the generic LDAP
044     * result elements as described in the {@link LDAPResult} class, but it may also
045     * include the following elements:
046     * <UL>
047     *   <LI>Response OID -- An optional OID that can be used to identify the type
048     *       of response.  This may be used if there can be different types of
049     *       responses for a given request.</LI>
050     *   <LI>Value -- An optional element that provides the encoded value for this
051     *       response.  If a value is provided, then the encoding for the value
052     *       depends on the type of extended result.</LI>
053     * </UL>
054     */
055    @Extensible()
056    @NotMutable()
057    @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
058    public class ExtendedResult
059           extends LDAPResult
060    {
061      /**
062       * The BER type for the extended response OID element.
063       */
064      private static final byte TYPE_EXTENDED_RESPONSE_OID = (byte) 0x8A;
065    
066    
067    
068      /**
069       * The BER type for the extended response value element.
070       */
071      private static final byte TYPE_EXTENDED_RESPONSE_VALUE = (byte) 0x8B;
072    
073    
074    
075      /**
076       * The serial version UID for this serializable class.
077       */
078      private static final long serialVersionUID = -6885923482396647963L;
079    
080    
081    
082      // The encoded value for this extended response, if available.
083      private final ASN1OctetString value;
084    
085      // The OID for this extended response, if available.
086      private final String oid;
087    
088    
089    
090      /**
091       * Creates a new extended result with the provided information.
092       *
093       * @param  messageID          The message ID for the LDAP message that is
094       *                            associated with this LDAP result.
095       * @param  resultCode         The result code from the response.
096       * @param  diagnosticMessage  The diagnostic message from the response, if
097       *                            available.
098       * @param  matchedDN          The matched DN from the response, if available.
099       * @param  referralURLs       The set of referral URLs from the response, if
100       *                            available.
101       * @param  oid                The OID for this extended response, if
102       *                            available.
103       * @param  value              The encoded value for this extended response, if
104       *                            available.
105       * @param  responseControls   The set of controls from the response, if
106       *                            available.
107       */
108      public ExtendedResult(final int messageID, final ResultCode resultCode,
109                            final String diagnosticMessage, final String matchedDN,
110                            final String[] referralURLs, final String oid,
111                            final ASN1OctetString value,
112                            final Control[] responseControls)
113      {
114        super(messageID, resultCode, diagnosticMessage, matchedDN, referralURLs,
115              responseControls);
116    
117        this.oid   = oid;
118        this.value = value;
119      }
120    
121    
122    
123      /**
124       * Creates a new extended result with the information contained in the
125       * provided LDAP result.  The extended result will not have an OID or value.
126       *
127       * @param  result  The LDAP result whose content should be used for this
128       *                 extended result.
129       */
130      public ExtendedResult(final LDAPResult result)
131      {
132        super(result);
133    
134        oid   = null;
135        value = null;
136      }
137    
138    
139    
140      /**
141       * Creates a new extended result from the provided {@code LDAPException}.
142       * The extended result will not have an OID or value.
143       *
144       * @param  exception  The {@code LDAPException} to use to create this extended
145       *                    result.
146       */
147      public ExtendedResult(final LDAPException exception)
148      {
149        this(exception.toLDAPResult());
150      }
151    
152    
153    
154      /**
155       * Creates a new extended result initialized from all of the elements of the
156       * provided extended response.
157       *
158       * @param  extendedResult  The extended response to use to initialize this
159       *                           extended response.
160       */
161      protected ExtendedResult(final ExtendedResult extendedResult)
162      {
163        this(extendedResult.getMessageID(), extendedResult.getResultCode(),
164             extendedResult.getDiagnosticMessage(), extendedResult.getMatchedDN(),
165             extendedResult.getReferralURLs(), extendedResult.getOID(),
166             extendedResult.getValue(), extendedResult.getResponseControls());
167      }
168    
169    
170    
171      /**
172       * Creates a new extended result object with the provided message ID and with
173       * the protocol op and controls read from the given ASN.1 stream reader.
174       *
175       * @param  messageID        The LDAP message ID for the LDAP message that is
176       *                          associated with this extended result.
177       * @param  messageSequence  The ASN.1 stream reader sequence used in the
178       *                          course of reading the LDAP message elements.
179       * @param  reader           The ASN.1 stream reader from which to read the
180       *                          protocol op and controls.
181       *
182       * @return  The decoded extended result.
183       *
184       * @throws  LDAPException  If a problem occurs while reading or decoding data
185       *                         from the ASN.1 stream reader.
186       */
187      static ExtendedResult readExtendedResultFrom(final int messageID,
188                                 final ASN1StreamReaderSequence messageSequence,
189                                 final ASN1StreamReader reader)
190             throws LDAPException
191      {
192        try
193        {
194          final ASN1StreamReaderSequence protocolOpSequence =
195               reader.beginSequence();
196          final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
197    
198          String matchedDN = reader.readString();
199          if (matchedDN.length() == 0)
200          {
201            matchedDN = null;
202          }
203    
204          String diagnosticMessage = reader.readString();
205          if (diagnosticMessage.length() == 0)
206          {
207            diagnosticMessage = null;
208          }
209    
210          String[] referralURLs = null;
211          String oid = null;
212          ASN1OctetString value = null;
213          while (protocolOpSequence.hasMoreElements())
214          {
215            final byte type = (byte) reader.peek();
216            switch (type)
217            {
218              case TYPE_REFERRAL_URLS:
219                final ArrayList<String> refList = new ArrayList<String>(1);
220                final ASN1StreamReaderSequence refSequence = reader.beginSequence();
221                while (refSequence.hasMoreElements())
222                {
223                  refList.add(reader.readString());
224                }
225                referralURLs = new String[refList.size()];
226                refList.toArray(referralURLs);
227                break;
228    
229              case TYPE_EXTENDED_RESPONSE_OID:
230                oid = reader.readString();
231                break;
232    
233              case TYPE_EXTENDED_RESPONSE_VALUE:
234                value = new ASN1OctetString(type, reader.readBytes());
235                break;
236    
237              default:
238                throw new LDAPException(ResultCode.DECODING_ERROR,
239                     ERR_EXTENDED_RESULT_INVALID_ELEMENT.get(toHex(type)));
240            }
241          }
242    
243          Control[] controls = NO_CONTROLS;
244          if (messageSequence.hasMoreElements())
245          {
246            final ArrayList<Control> controlList = new ArrayList<Control>(1);
247            final ASN1StreamReaderSequence controlSequence = reader.beginSequence();
248            while (controlSequence.hasMoreElements())
249            {
250              controlList.add(Control.readFrom(reader));
251            }
252    
253            controls = new Control[controlList.size()];
254            controlList.toArray(controls);
255          }
256    
257          return new ExtendedResult(messageID, resultCode, diagnosticMessage,
258                                    matchedDN, referralURLs, oid, value, controls);
259        }
260        catch (LDAPException le)
261        {
262          debugException(le);
263          throw le;
264        }
265        catch (Exception e)
266        {
267          debugException(e);
268          throw new LDAPException(ResultCode.DECODING_ERROR,
269               ERR_EXTENDED_RESULT_CANNOT_DECODE.get(getExceptionMessage(e)), e);
270        }
271      }
272    
273    
274    
275      /**
276       * Retrieves the OID for this extended result, if available.
277       *
278       * @return  The OID for this extended result, or {@code null} if none is
279       *          available.
280       */
281      public final String getOID()
282      {
283        return oid;
284      }
285    
286    
287    
288      /**
289       * Indicates whether this extended result has a value.
290       *
291       * @return  {@code true} if this extended result has a value, or
292       *          {@code false} if not.
293       */
294      public final boolean hasValue()
295      {
296        return (value != null);
297      }
298    
299    
300    
301      /**
302       * Retrieves the encoded value for this extended result, if available.
303       *
304       * @return  The encoded value for this extended result, or {@code null} if
305       *          none is available.
306       */
307      public final ASN1OctetString getValue()
308      {
309        return value;
310      }
311    
312    
313    
314      /**
315       * Retrieves the user-friendly name for the extended result, if available.
316       * If no user-friendly name has been defined, but a response OID is available,
317       * then that will be returned.  If neither a user-friendly name nor a response
318       * OID are available, then {@code null} will be returned.
319       *
320       * @return  The user-friendly name for this extended request, the response OID
321       *          if a user-friendly name is not available but a response OID is, or
322       *          {@code null} if neither a user-friendly name nor a response OID
323       *          are available.
324       */
325      public String getExtendedResultName()
326      {
327        // By default, we will return the OID (which may be null).  Subclasses
328        // should override this to provide the user-friendly name.
329        return oid;
330      }
331    
332    
333    
334      /**
335       * Retrieves a string representation of this extended response.
336       *
337       * @return  A string representation of this extended response.
338       */
339      @Override()
340      public String toString()
341      {
342        final StringBuilder buffer = new StringBuilder();
343        toString(buffer);
344        return buffer.toString();
345      }
346    
347    
348    
349      /**
350       * Appends a string representation of this extended response to the provided
351       * buffer.
352       *
353       * @param  buffer  The buffer to which a string representation of this
354       *                 extended response will be appended.
355       */
356      @Override()
357      public void toString(final StringBuilder buffer)
358      {
359        buffer.append("ExtendedResult(resultCode=");
360        buffer.append(getResultCode());
361    
362        final int messageID = getMessageID();
363        if (messageID >= 0)
364        {
365          buffer.append(", messageID=");
366          buffer.append(messageID);
367        }
368    
369        final String diagnosticMessage = getDiagnosticMessage();
370        if (diagnosticMessage != null)
371        {
372          buffer.append(", diagnosticMessage='");
373          buffer.append(diagnosticMessage);
374          buffer.append('\'');
375        }
376    
377        final String matchedDN = getMatchedDN();
378        if (matchedDN != null)
379        {
380          buffer.append(", matchedDN='");
381          buffer.append(matchedDN);
382          buffer.append('\'');
383        }
384    
385        final String[] referralURLs = getReferralURLs();
386        if (referralURLs.length > 0)
387        {
388          buffer.append(", referralURLs={");
389          for (int i=0; i < referralURLs.length; i++)
390          {
391            if (i > 0)
392            {
393              buffer.append(", ");
394            }
395    
396            buffer.append(referralURLs[i]);
397          }
398          buffer.append('}');
399        }
400    
401        if (oid != null)
402        {
403          buffer.append(", oid=");
404          buffer.append(oid);
405        }
406    
407        final Control[] responseControls = getResponseControls();
408        if (responseControls.length > 0)
409        {
410          buffer.append(", responseControls={");
411          for (int i=0; i < responseControls.length; i++)
412          {
413            if (i > 0)
414            {
415              buffer.append(", ");
416            }
417    
418            buffer.append(responseControls[i]);
419          }
420          buffer.append('}');
421        }
422    
423        buffer.append(')');
424      }
425    }