001/*
002 * Copyright 2009-2014 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2009-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 */
021package com.unboundid.ldap.protocol;
022
023
024
025import java.util.ArrayList;
026import java.util.Collections;
027import java.util.Iterator;
028import java.util.List;
029
030import com.unboundid.asn1.ASN1Buffer;
031import com.unboundid.asn1.ASN1BufferSequence;
032import com.unboundid.asn1.ASN1Element;
033import com.unboundid.asn1.ASN1Enumerated;
034import com.unboundid.asn1.ASN1OctetString;
035import com.unboundid.asn1.ASN1Sequence;
036import com.unboundid.asn1.ASN1StreamReader;
037import com.unboundid.asn1.ASN1StreamReaderSequence;
038import com.unboundid.ldap.sdk.Control;
039import com.unboundid.ldap.sdk.ExtendedResult;
040import com.unboundid.ldap.sdk.LDAPException;
041import com.unboundid.ldap.sdk.LDAPResult;
042import com.unboundid.ldap.sdk.ResultCode;
043import com.unboundid.util.NotMutable;
044import com.unboundid.util.InternalUseOnly;
045import com.unboundid.util.ThreadSafety;
046import com.unboundid.util.ThreadSafetyLevel;
047
048import static com.unboundid.ldap.protocol.ProtocolMessages.*;
049import static com.unboundid.util.Debug.*;
050import static com.unboundid.util.StaticUtils.*;
051import static com.unboundid.util.Validator.*;
052
053
054
055/**
056 * This class provides an implementation of a extended response protocol op.
057 */
058@InternalUseOnly()
059@NotMutable()
060@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
061public final class ExtendedResponseProtocolOp
062       implements ProtocolOp
063{
064  /**
065   * The BER type for the response OID element.
066   */
067  public static final byte TYPE_RESPONSE_OID = (byte) 0x8A;
068
069
070
071  /**
072   * The BER type for the response value element.
073   */
074  public static final byte TYPE_RESPONSE_VALUE = (byte) 0x8B;
075
076
077
078  /**
079   * The serial version UID for this serializable class.
080   */
081  private static final long serialVersionUID = -7757619031268544913L;
082
083
084
085  // The value for this extended response.
086  private final ASN1OctetString responseValue;
087
088  // The result code for this extended response.
089  private final int resultCode;
090
091  // The referral URLs for this extended response.
092  private final List<String> referralURLs;
093
094  // The diagnostic message for this extended response.
095  private final String diagnosticMessage;
096
097  // The matched DN for this extended response.
098  private final String matchedDN;
099
100  // The OID for this extended response.
101  private final String responseOID;
102
103
104
105  /**
106   * Creates a new instance of this extended response protocol op with the
107   * provided information.
108   *
109   * @param  resultCode         The result code for this response.
110   * @param  matchedDN          The matched DN for this response, if available.
111   * @param  diagnosticMessage  The diagnostic message for this response, if
112   *                            any.
113   * @param  referralURLs       The list of referral URLs for this response, if
114   *                            any.
115   * @param  responseOID        The response OID for this response, if any.
116   * @param  responseValue      The value for this response, if any.
117   */
118  public ExtendedResponseProtocolOp(final int resultCode,
119                                    final String matchedDN,
120                                    final String diagnosticMessage,
121                                    final List<String> referralURLs,
122                                    final String responseOID,
123                                    final ASN1OctetString responseValue)
124  {
125    this.resultCode        = resultCode;
126    this.matchedDN         = matchedDN;
127    this.diagnosticMessage = diagnosticMessage;
128    this.responseOID       = responseOID;
129
130    if (referralURLs == null)
131    {
132      this.referralURLs = Collections.emptyList();
133    }
134    else
135    {
136      this.referralURLs = Collections.unmodifiableList(referralURLs);
137    }
138
139    if (responseValue == null)
140    {
141      this.responseValue = null;
142    }
143    else
144    {
145      this.responseValue =
146           new ASN1OctetString(TYPE_RESPONSE_VALUE, responseValue.getValue());
147    }
148  }
149
150
151
152  /**
153   * Creates a new extended response protocol op from the provided extended
154   * result object.
155   *
156   * @param  result  The extended result object to use to create this protocol
157   *                 op.
158   */
159  public ExtendedResponseProtocolOp(final LDAPResult result)
160  {
161    resultCode        = result.getResultCode().intValue();
162    matchedDN         = result.getMatchedDN();
163    diagnosticMessage = result.getDiagnosticMessage();
164    referralURLs      = toList(result.getReferralURLs());
165
166    if (result instanceof ExtendedResult)
167    {
168      final ExtendedResult r = (ExtendedResult) result;
169      responseOID   = r.getOID();
170      responseValue = r.getValue();
171    }
172    else
173    {
174      responseOID   = null;
175      responseValue = null;
176    }
177  }
178
179
180
181  /**
182   * Creates a new extended response protocol op read from the provided ASN.1
183   * stream reader.
184   *
185   * @param  reader  The ASN.1 stream reader from which to read the extended
186   *                 response.
187   *
188   * @throws  LDAPException  If a problem occurs while reading or parsing the
189   *                         extended response.
190   */
191  ExtendedResponseProtocolOp(final ASN1StreamReader reader)
192       throws LDAPException
193  {
194    try
195    {
196      final ASN1StreamReaderSequence opSequence = reader.beginSequence();
197      resultCode = reader.readEnumerated();
198
199      String s = reader.readString();
200      ensureNotNull(s);
201      if (s.length() == 0)
202      {
203        matchedDN = null;
204      }
205      else
206      {
207        matchedDN = s;
208      }
209
210      s = reader.readString();
211      ensureNotNull(s);
212      if (s.length() == 0)
213      {
214        diagnosticMessage = null;
215      }
216      else
217      {
218        diagnosticMessage = s;
219      }
220
221      ASN1OctetString value = null;
222      String oid = null;
223      final ArrayList<String> refs = new ArrayList<String>(1);
224      while (opSequence.hasMoreElements())
225      {
226        final byte type = (byte) reader.peek();
227        if (type == GenericResponseProtocolOp.TYPE_REFERRALS)
228        {
229          final ASN1StreamReaderSequence refSequence = reader.beginSequence();
230          while (refSequence.hasMoreElements())
231          {
232            refs.add(reader.readString());
233          }
234        }
235        else if (type == TYPE_RESPONSE_OID)
236        {
237          oid = reader.readString();
238        }
239        else if (type == TYPE_RESPONSE_VALUE)
240        {
241          value = new ASN1OctetString(type, reader.readBytes());
242        }
243        else
244        {
245          throw new LDAPException(ResultCode.DECODING_ERROR,
246               ERR_EXTENDED_RESPONSE_INVALID_ELEMENT.get(toHex(type)));
247        }
248      }
249
250      referralURLs  = Collections.unmodifiableList(refs);
251      responseOID   = oid;
252      responseValue = value;
253    }
254    catch (LDAPException le)
255    {
256      debugException(le);
257      throw le;
258    }
259    catch (Exception e)
260    {
261      debugException(e);
262      throw new LDAPException(ResultCode.DECODING_ERROR,
263           ERR_EXTENDED_RESPONSE_CANNOT_DECODE.get(getExceptionMessage(e)), e);
264    }
265  }
266
267
268
269  /**
270   * Retrieves the result code for this extended response.
271   *
272   * @return  The result code for this extended response.
273   */
274  public int getResultCode()
275  {
276    return resultCode;
277  }
278
279
280
281  /**
282   * Retrieves the matched DN for this extended response, if any.
283   *
284   * @return  The matched DN for this extended response, or {@code null} if
285   *          there is no matched DN.
286   */
287  public String getMatchedDN()
288  {
289    return matchedDN;
290  }
291
292
293
294  /**
295   * Retrieves the diagnostic message for this extended response, if any.
296   *
297   * @return  The diagnostic message for this extended response, or {@code null}
298   *          if there is no diagnostic message.
299   */
300  public String getDiagnosticMessage()
301  {
302    return diagnosticMessage;
303  }
304
305
306
307  /**
308   * Retrieves the list of referral URLs for this extended response.
309   *
310   * @return  The list of referral URLs for this extended response, or an empty
311   *          list if there are no referral URLs.
312   */
313  public List<String> getReferralURLs()
314  {
315    return referralURLs;
316  }
317
318
319
320  /**
321   * Retrieves the OID for this extended response, if any.
322   *
323   * @return  The OID for this extended response, or {@code null} if there is no
324   *          response OID.
325   */
326  public String getResponseOID()
327  {
328    return responseOID;
329  }
330
331
332
333  /**
334   * Retrieves the value for this extended response, if any.
335   *
336   * @return  The value for this extended response, or {@code null} if there is
337   *          no response value.
338   */
339  public ASN1OctetString getResponseValue()
340  {
341    return responseValue;
342  }
343
344
345
346  /**
347   * {@inheritDoc}
348   */
349  public byte getProtocolOpType()
350  {
351    return LDAPMessage.PROTOCOL_OP_TYPE_EXTENDED_RESPONSE;
352  }
353
354
355
356  /**
357   * {@inheritDoc}
358   */
359  public ASN1Element encodeProtocolOp()
360  {
361    final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(6);
362    elements.add(new ASN1Enumerated(getResultCode()));
363
364    final String mdn = getMatchedDN();
365    if (mdn == null)
366    {
367      elements.add(new ASN1OctetString());
368    }
369    else
370    {
371      elements.add(new ASN1OctetString(mdn));
372    }
373
374    final String dm = getDiagnosticMessage();
375    if (dm == null)
376    {
377      elements.add(new ASN1OctetString());
378    }
379    else
380    {
381      elements.add(new ASN1OctetString(dm));
382    }
383
384    final List<String> refs = getReferralURLs();
385    if (! refs.isEmpty())
386    {
387      final ArrayList<ASN1Element> refElements =
388           new ArrayList<ASN1Element>(refs.size());
389      for (final String r : refs)
390      {
391        refElements.add(new ASN1OctetString(r));
392      }
393      elements.add(new ASN1Sequence(GenericResponseProtocolOp.TYPE_REFERRALS,
394           refElements));
395    }
396
397    if (responseOID != null)
398    {
399      elements.add(new ASN1OctetString(TYPE_RESPONSE_OID, responseOID));
400    }
401
402    if (responseValue != null)
403    {
404      elements.add(responseValue);
405    }
406
407    return new ASN1Sequence(LDAPMessage.PROTOCOL_OP_TYPE_EXTENDED_RESPONSE,
408         elements);
409  }
410
411
412
413  /**
414   * Decodes the provided ASN.1 element as an extended response protocol op.
415   *
416   * @param  element  The ASN.1 element to be decoded.
417   *
418   * @return  The decoded extended response protocol op.
419   *
420   * @throws  LDAPException  If the provided ASN.1 element cannot be decoded as
421   *                         an extended response protocol op.
422   */
423  public static ExtendedResponseProtocolOp decodeProtocolOp(
424                                                final ASN1Element element)
425         throws LDAPException
426  {
427    try
428    {
429      final ASN1Element[] elements =
430           ASN1Sequence.decodeAsSequence(element).elements();
431      final int resultCode =
432           ASN1Enumerated.decodeAsEnumerated(elements[0]).intValue();
433
434      final String matchedDN;
435      final String md =
436           ASN1OctetString.decodeAsOctetString(elements[1]).stringValue();
437      if (md.length() > 0)
438      {
439        matchedDN = md;
440      }
441      else
442      {
443        matchedDN = null;
444      }
445
446      final String diagnosticMessage;
447      final String dm =
448           ASN1OctetString.decodeAsOctetString(elements[2]).stringValue();
449      if (dm.length() > 0)
450      {
451        diagnosticMessage = dm;
452      }
453      else
454      {
455        diagnosticMessage = null;
456      }
457
458      ASN1OctetString responseValue = null;
459      List<String> referralURLs = null;
460      String responseOID = null;
461      if (elements.length > 3)
462      {
463        for (int i=3; i < elements.length; i++)
464        {
465          switch (elements[i].getType())
466          {
467            case GenericResponseProtocolOp.TYPE_REFERRALS:
468              final ASN1Element[] refElements =
469                   ASN1Sequence.decodeAsSequence(elements[3]).elements();
470              referralURLs = new ArrayList<String>(refElements.length);
471              for (final ASN1Element e : refElements)
472              {
473                referralURLs.add(
474                     ASN1OctetString.decodeAsOctetString(e).stringValue());
475              }
476              break;
477
478            case TYPE_RESPONSE_OID:
479              responseOID = ASN1OctetString.decodeAsOctetString(elements[i]).
480                   stringValue();
481              break;
482
483            case TYPE_RESPONSE_VALUE:
484              responseValue = ASN1OctetString.decodeAsOctetString(elements[i]);
485              break;
486
487            default:
488              throw new LDAPException(ResultCode.DECODING_ERROR,
489                   ERR_EXTENDED_RESPONSE_INVALID_ELEMENT.get(
490                        toHex(elements[i].getType())));
491          }
492        }
493      }
494
495      return new ExtendedResponseProtocolOp(resultCode, matchedDN,
496           diagnosticMessage, referralURLs, responseOID, responseValue);
497    }
498    catch (final LDAPException le)
499    {
500      debugException(le);
501      throw le;
502    }
503    catch (final Exception e)
504    {
505      debugException(e);
506      throw new LDAPException(ResultCode.DECODING_ERROR,
507           ERR_EXTENDED_RESPONSE_CANNOT_DECODE.get(getExceptionMessage(e)),
508           e);
509    }
510  }
511
512
513
514  /**
515   * {@inheritDoc}
516   */
517  public void writeTo(final ASN1Buffer buffer)
518  {
519    final ASN1BufferSequence opSequence =
520         buffer.beginSequence(LDAPMessage.PROTOCOL_OP_TYPE_EXTENDED_RESPONSE);
521    buffer.addEnumerated(resultCode);
522    buffer.addOctetString(matchedDN);
523    buffer.addOctetString(diagnosticMessage);
524
525    if (! referralURLs.isEmpty())
526    {
527      final ASN1BufferSequence refSequence =
528           buffer.beginSequence(GenericResponseProtocolOp.TYPE_REFERRALS);
529      for (final String s : referralURLs)
530      {
531        buffer.addOctetString(s);
532      }
533      refSequence.end();
534    }
535
536    if (responseOID != null)
537    {
538      buffer.addOctetString(TYPE_RESPONSE_OID, responseOID);
539    }
540
541    if (responseValue != null)
542    {
543      buffer.addOctetString(TYPE_RESPONSE_VALUE, responseValue.getValue());
544    }
545
546    opSequence.end();
547  }
548
549
550
551  /**
552   * Creates a extended result from this protocol op.
553   *
554   * @param  controls  The set of controls to include in the extended result.
555   *                   It may be empty or {@code null} if no controls should be
556   *                   included.
557   *
558   * @return  The extended result that was created.
559   */
560  public ExtendedResult toExtendedResult(final Control... controls)
561  {
562    final String[] referralArray = new String[referralURLs.size()];
563    referralURLs.toArray(referralArray);
564
565    return new ExtendedResult(-1, ResultCode.valueOf(resultCode),
566         diagnosticMessage, matchedDN, referralArray, responseOID,
567         responseValue, controls);
568  }
569
570
571
572  /**
573   * Retrieves a string representation of this protocol op.
574   *
575   * @return  A string representation of this protocol op.
576   */
577  @Override()
578  public String toString()
579  {
580    final StringBuilder buffer = new StringBuilder();
581    toString(buffer);
582    return buffer.toString();
583  }
584
585
586
587  /**
588   * {@inheritDoc}
589   */
590  public void toString(final StringBuilder buffer)
591  {
592    buffer.append("ExtendedResponseProtocolOp(resultCode=");
593    buffer.append(resultCode);
594
595    if (matchedDN != null)
596    {
597      buffer.append(", matchedDN='");
598      buffer.append(matchedDN);
599      buffer.append('\'');
600    }
601
602    if (diagnosticMessage != null)
603    {
604      buffer.append(", diagnosticMessage='");
605      buffer.append(diagnosticMessage);
606      buffer.append('\'');
607    }
608
609    if (! referralURLs.isEmpty())
610    {
611      buffer.append(", referralURLs={");
612
613      final Iterator<String> iterator = referralURLs.iterator();
614      while (iterator.hasNext())
615      {
616        buffer.append('\'');
617        buffer.append(iterator.next());
618        buffer.append('\'');
619        if (iterator.hasNext())
620        {
621          buffer.append(',');
622        }
623      }
624
625      buffer.append('}');
626    }
627
628    if (responseOID != null)
629    {
630      buffer.append(", responseOID='");
631      buffer.append(responseOID);
632      buffer.append('\'');
633    }
634
635    buffer.append(')');
636  }
637}