001    /*
002     * Copyright 2007-2013 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2008-2013 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 initialized from all of the elements of the
142       * provided extended response.
143       *
144       * @param  extendedResult  The extended response to use to initialize this
145       *                           extended response.
146       */
147      protected ExtendedResult(final ExtendedResult extendedResult)
148      {
149        this(extendedResult.getMessageID(), extendedResult.getResultCode(),
150             extendedResult.getDiagnosticMessage(), extendedResult.getMatchedDN(),
151             extendedResult.getReferralURLs(), extendedResult.getOID(),
152             extendedResult.getValue(), extendedResult.getResponseControls());
153      }
154    
155    
156    
157      /**
158       * Creates a new extended result object with the provided message ID and with
159       * the protocol op and controls read from the given ASN.1 stream reader.
160       *
161       * @param  messageID        The LDAP message ID for the LDAP message that is
162       *                          associated with this extended result.
163       * @param  messageSequence  The ASN.1 stream reader sequence used in the
164       *                          course of reading the LDAP message elements.
165       * @param  reader           The ASN.1 stream reader from which to read the
166       *                          protocol op and controls.
167       *
168       * @return  The decoded extended result.
169       *
170       * @throws  LDAPException  If a problem occurs while reading or decoding data
171       *                         from the ASN.1 stream reader.
172       */
173      static ExtendedResult readExtendedResultFrom(final int messageID,
174                                 final ASN1StreamReaderSequence messageSequence,
175                                 final ASN1StreamReader reader)
176             throws LDAPException
177      {
178        try
179        {
180          final ASN1StreamReaderSequence protocolOpSequence =
181               reader.beginSequence();
182          final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
183    
184          String matchedDN = reader.readString();
185          if (matchedDN.length() == 0)
186          {
187            matchedDN = null;
188          }
189    
190          String diagnosticMessage = reader.readString();
191          if (diagnosticMessage.length() == 0)
192          {
193            diagnosticMessage = null;
194          }
195    
196          String[] referralURLs = null;
197          String oid = null;
198          ASN1OctetString value = null;
199          while (protocolOpSequence.hasMoreElements())
200          {
201            final byte type = (byte) reader.peek();
202            switch (type)
203            {
204              case TYPE_REFERRAL_URLS:
205                final ArrayList<String> refList = new ArrayList<String>(1);
206                final ASN1StreamReaderSequence refSequence = reader.beginSequence();
207                while (refSequence.hasMoreElements())
208                {
209                  refList.add(reader.readString());
210                }
211                referralURLs = new String[refList.size()];
212                refList.toArray(referralURLs);
213                break;
214    
215              case TYPE_EXTENDED_RESPONSE_OID:
216                oid = reader.readString();
217                break;
218    
219              case TYPE_EXTENDED_RESPONSE_VALUE:
220                value = new ASN1OctetString(type, reader.readBytes());
221                break;
222    
223              default:
224                throw new LDAPException(ResultCode.DECODING_ERROR,
225                     ERR_EXTENDED_RESULT_INVALID_ELEMENT.get(toHex(type)));
226            }
227          }
228    
229          Control[] controls = NO_CONTROLS;
230          if (messageSequence.hasMoreElements())
231          {
232            final ArrayList<Control> controlList = new ArrayList<Control>(1);
233            final ASN1StreamReaderSequence controlSequence = reader.beginSequence();
234            while (controlSequence.hasMoreElements())
235            {
236              controlList.add(Control.readFrom(reader));
237            }
238    
239            controls = new Control[controlList.size()];
240            controlList.toArray(controls);
241          }
242    
243          return new ExtendedResult(messageID, resultCode, diagnosticMessage,
244                                    matchedDN, referralURLs, oid, value, controls);
245        }
246        catch (LDAPException le)
247        {
248          debugException(le);
249          throw le;
250        }
251        catch (Exception e)
252        {
253          debugException(e);
254          throw new LDAPException(ResultCode.DECODING_ERROR,
255               ERR_EXTENDED_RESULT_CANNOT_DECODE.get(getExceptionMessage(e)), e);
256        }
257      }
258    
259    
260    
261      /**
262       * Retrieves the OID for this extended result, if available.
263       *
264       * @return  The OID for this extended result, or {@code null} if none is
265       *          available.
266       */
267      public final String getOID()
268      {
269        return oid;
270      }
271    
272    
273    
274      /**
275       * Indicates whether this extended result has a value.
276       *
277       * @return  {@code true} if this extended result has a value, or
278       *          {@code false} if not.
279       */
280      public final boolean hasValue()
281      {
282        return (value != null);
283      }
284    
285    
286    
287      /**
288       * Retrieves the encoded value for this extended result, if available.
289       *
290       * @return  The encoded value for this extended result, or {@code null} if
291       *          none is available.
292       */
293      public final ASN1OctetString getValue()
294      {
295        return value;
296      }
297    
298    
299    
300      /**
301       * Retrieves the user-friendly name for the extended result, if available.
302       * If no user-friendly name has been defined, but a response OID is available,
303       * then that will be returned.  If neither a user-friendly name nor a response
304       * OID are available, then {@code null} will be returned.
305       *
306       * @return  The user-friendly name for this extended request, the response OID
307       *          if a user-friendly name is not available but a response OID is, or
308       *          {@code null} if neither a user-friendly name nor a response OID
309       *          are available.
310       */
311      public String getExtendedResultName()
312      {
313        // By default, we will return the OID (which may be null).  Subclasses
314        // should override this to provide the user-friendly name.
315        return oid;
316      }
317    
318    
319    
320      /**
321       * Retrieves a string representation of this extended response.
322       *
323       * @return  A string representation of this extended response.
324       */
325      @Override()
326      public String toString()
327      {
328        final StringBuilder buffer = new StringBuilder();
329        toString(buffer);
330        return buffer.toString();
331      }
332    
333    
334    
335      /**
336       * Appends a string representation of this extended response to the provided
337       * buffer.
338       *
339       * @param  buffer  The buffer to which a string representation of this
340       *                 extended response will be appended.
341       */
342      @Override()
343      public void toString(final StringBuilder buffer)
344      {
345        buffer.append("ExtendedResult(resultCode=");
346        buffer.append(getResultCode());
347    
348        final int messageID = getMessageID();
349        if (messageID >= 0)
350        {
351          buffer.append(", messageID=");
352          buffer.append(messageID);
353        }
354    
355        final String diagnosticMessage = getDiagnosticMessage();
356        if (diagnosticMessage != null)
357        {
358          buffer.append(", diagnosticMessage='");
359          buffer.append(diagnosticMessage);
360          buffer.append('\'');
361        }
362    
363        final String matchedDN = getMatchedDN();
364        if (matchedDN != null)
365        {
366          buffer.append(", matchedDN='");
367          buffer.append(matchedDN);
368          buffer.append('\'');
369        }
370    
371        final String[] referralURLs = getReferralURLs();
372        if (referralURLs.length > 0)
373        {
374          buffer.append(", referralURLs={");
375          for (int i=0; i < referralURLs.length; i++)
376          {
377            if (i > 0)
378            {
379              buffer.append(", ");
380            }
381    
382            buffer.append(referralURLs[i]);
383          }
384          buffer.append('}');
385        }
386    
387        if (oid != null)
388        {
389          buffer.append(", oid=");
390          buffer.append(oid);
391        }
392    
393        final Control[] responseControls = getResponseControls();
394        if (responseControls.length > 0)
395        {
396          buffer.append(", responseControls={");
397          for (int i=0; i < responseControls.length; i++)
398          {
399            if (i > 0)
400            {
401              buffer.append(", ");
402            }
403    
404            buffer.append(responseControls[i]);
405          }
406          buffer.append('}');
407        }
408    
409        buffer.append(')');
410      }
411    }