001    /*
002     * Copyright 2009-2015 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2009-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.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.ASN1StreamReader;
033    import com.unboundid.asn1.ASN1StreamReaderSequence;
034    import com.unboundid.ldap.sdk.Control;
035    import com.unboundid.ldap.sdk.LDAPException;
036    import com.unboundid.ldap.sdk.LDAPResult;
037    import com.unboundid.ldap.sdk.ResultCode;
038    import com.unboundid.util.InternalUseOnly;
039    import com.unboundid.util.NotExtensible;
040    import com.unboundid.util.ThreadSafety;
041    import com.unboundid.util.ThreadSafetyLevel;
042    
043    import static com.unboundid.ldap.protocol.ProtocolMessages.*;
044    import static com.unboundid.util.Debug.*;
045    import static com.unboundid.util.StaticUtils.*;
046    import static com.unboundid.util.Validator.*;
047    
048    
049    
050    /**
051     * This class provides an implementation of a generic response protocol op.
052     * It must be subclassed by classes providing implementations for each
053     * operation type.
054     */
055    @InternalUseOnly()
056    @NotExtensible()
057    @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
058    public abstract class GenericResponseProtocolOp
059           implements ProtocolOp
060    {
061      /**
062       * The BER type for the referral URLs elements.
063       */
064      public static final byte TYPE_REFERRALS = (byte) 0xA3;
065    
066    
067    
068      /**
069       * The serial version UID for this serializable class.
070       */
071      private static final long serialVersionUID = 3837308973105414874L;
072    
073    
074    
075      // The BER type for this response.
076      private final byte type;
077    
078      // The result code for this response.
079      private final int resultCode;
080    
081      // The referral URLs for this response.
082      private final List<String> referralURLs;
083    
084      // The diagnostic message for this response.
085      private final String diagnosticMessage;
086    
087      // The matched DN for this response.Static
088      private final String matchedDN;
089    
090    
091    
092      /**
093       * Creates a new instance of this response with the provided information.
094       *
095       * @param  type               The BER type for this response.
096       * @param  resultCode         The result code for this response.
097       * @param  matchedDN          The matched DN for this result, if available.
098       * @param  diagnosticMessage  The diagnostic message for this response, if
099       *                            available.
100       * @param  referralURLs       The list of referral URLs for this response, if
101       *                            available.
102       */
103      protected GenericResponseProtocolOp(final byte type, final int resultCode,
104                                        final String matchedDN,
105                                        final String diagnosticMessage,
106                                        final List<String> referralURLs)
107      {
108        this.type              = type;
109        this.resultCode        = resultCode;
110        this.matchedDN         = matchedDN;
111        this.diagnosticMessage = diagnosticMessage;
112    
113        if (referralURLs == null)
114        {
115          this.referralURLs = Collections.emptyList();
116        }
117        else
118        {
119          this.referralURLs = Collections.unmodifiableList(referralURLs);
120        }
121      }
122    
123    
124    
125      /**
126       * Creates a new response read from the provided ASN.1 stream reader.
127       *
128       * @param  reader  The ASN.1 stream reader from which to read the response.
129       *
130       * @throws  LDAPException  If a problem occurs while reading or parsing the
131       *                         response.
132       */
133      protected GenericResponseProtocolOp(final ASN1StreamReader reader)
134                throws LDAPException
135      {
136        try
137        {
138          type = (byte) reader.peek();
139          final ASN1StreamReaderSequence opSequence = reader.beginSequence();
140          resultCode = reader.readEnumerated();
141    
142          String s = reader.readString();
143          ensureNotNull(s);
144          if (s.length() == 0)
145          {
146            matchedDN = null;
147          }
148          else
149          {
150            matchedDN = s;
151          }
152    
153          s = reader.readString();
154          ensureNotNull(s);
155          if (s.length() == 0)
156          {
157            diagnosticMessage = null;
158          }
159          else
160          {
161            diagnosticMessage = s;
162          }
163    
164          if (opSequence.hasMoreElements())
165          {
166            final ArrayList<String> refs = new ArrayList<String>(1);
167            final ASN1StreamReaderSequence refSequence = reader.beginSequence();
168            while (refSequence.hasMoreElements())
169            {
170              refs.add(reader.readString());
171            }
172            referralURLs = Collections.unmodifiableList(refs);
173          }
174          else
175          {
176            referralURLs = Collections.emptyList();
177          }
178        }
179        catch (Exception e)
180        {
181          debugException(e);
182          throw new LDAPException(ResultCode.DECODING_ERROR,
183               ERR_RESPONSE_CANNOT_DECODE.get(getExceptionMessage(e)), e);
184        }
185      }
186    
187    
188    
189      /**
190       * Retrieves the result code for this response.
191       *
192       * @return  The result code for this response.
193       */
194      public final int getResultCode()
195      {
196        return resultCode;
197      }
198    
199    
200    
201      /**
202       * Retrieves the matched DN for this response, if any.
203       *
204       * @return  The matched DN for this response, or {@code null} if there is
205       *          no matched DN.
206       */
207      public final String getMatchedDN()
208      {
209        return matchedDN;
210      }
211    
212    
213    
214      /**
215       * Retrieves the diagnostic message for this response, if any.
216       *
217       * @return  The diagnostic message for this response, or {@code null} if there
218       *          is no diagnostic message.
219       */
220      public final String getDiagnosticMessage()
221      {
222        return diagnosticMessage;
223      }
224    
225    
226    
227      /**
228       * Retrieves the list of referral URLs for this response.
229       *
230       * @return  The list of referral URLs for this response, or an empty list
231       *          if there are no referral URLs.
232       */
233      public final List<String> getReferralURLs()
234      {
235        return referralURLs;
236      }
237    
238    
239    
240      /**
241       * {@inheritDoc}
242       */
243      public byte getProtocolOpType()
244      {
245        return type;
246      }
247    
248    
249    
250      /**
251       * {@inheritDoc}
252       */
253      public final void writeTo(final ASN1Buffer buffer)
254      {
255        final ASN1BufferSequence opSequence = buffer.beginSequence(type);
256        buffer.addEnumerated(resultCode);
257        buffer.addOctetString(matchedDN);
258        buffer.addOctetString(diagnosticMessage);
259    
260        if (! referralURLs.isEmpty())
261        {
262          final ASN1BufferSequence refSequence =
263               buffer.beginSequence(TYPE_REFERRALS);
264          for (final String s : referralURLs)
265          {
266            buffer.addOctetString(s);
267          }
268          refSequence.end();
269        }
270        opSequence.end();
271      }
272    
273    
274    
275      /**
276       * Creates a new LDAP result object from this response protocol op.
277       *
278       * @param  controls  The set of controls to include in the LDAP result.  It
279       *                   may be empty or {@code null} if no controls should be
280       *                   included.
281       *
282       * @return  The LDAP result that was created.
283       */
284      public LDAPResult toLDAPResult(final Control... controls)
285      {
286        final String[] refs;
287        if (referralURLs.isEmpty())
288        {
289          refs = NO_STRINGS;
290        }
291        else
292        {
293          refs = new String[referralURLs.size()];
294          referralURLs.toArray(refs);
295        }
296    
297        return new LDAPResult(-1, ResultCode.valueOf(resultCode), diagnosticMessage,
298             matchedDN, refs, controls);
299      }
300    
301    
302    
303      /**
304       * Retrieves a string representation of this protocol op.
305       *
306       * @return  A string representation of this protocol op.
307       */
308      @Override()
309      public final String toString()
310      {
311        final StringBuilder buffer = new StringBuilder();
312        toString(buffer);
313        return buffer.toString();
314      }
315    
316    
317    
318      /**
319       * {@inheritDoc}
320       */
321      public final void toString(final StringBuilder buffer)
322      {
323        buffer.append("ResponseProtocolOp(type=");
324        toHex(type, buffer);
325        buffer.append(", resultCode=");
326        buffer.append(resultCode);
327    
328        if (matchedDN != null)
329        {
330          buffer.append(", matchedDN='");
331          buffer.append(matchedDN);
332          buffer.append('\'');
333        }
334    
335        if (diagnosticMessage != null)
336        {
337          buffer.append(", diagnosticMessage='");
338          buffer.append(diagnosticMessage);
339          buffer.append('\'');
340        }
341    
342        if (! referralURLs.isEmpty())
343        {
344          buffer.append(", referralURLs={");
345    
346          final Iterator<String> iterator = referralURLs.iterator();
347          while (iterator.hasNext())
348          {
349            buffer.append('\'');
350            buffer.append(iterator.next());
351            buffer.append('\'');
352            if (iterator.hasNext())
353            {
354              buffer.append(',');
355            }
356          }
357    
358          buffer.append('}');
359        }
360        buffer.append(')');
361      }
362    }