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