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