001/*
002 * Copyright 2009-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2009-2024 Ping Identity Corporation
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *    http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020/*
021 * Copyright (C) 2009-2024 Ping Identity Corporation
022 *
023 * This program is free software; you can redistribute it and/or modify
024 * it under the terms of the GNU General Public License (GPLv2 only)
025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
026 * as published by the Free Software Foundation.
027 *
028 * This program is distributed in the hope that it will be useful,
029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
031 * GNU General Public License for more details.
032 *
033 * You should have received a copy of the GNU General Public License
034 * along with this program; if not, see <http://www.gnu.org/licenses>.
035 */
036package com.unboundid.ldap.protocol;
037
038
039
040import java.util.ArrayList;
041import java.util.Collections;
042import java.util.Iterator;
043import java.util.List;
044
045import com.unboundid.asn1.ASN1Buffer;
046import com.unboundid.asn1.ASN1BufferSequence;
047import com.unboundid.asn1.ASN1StreamReader;
048import com.unboundid.asn1.ASN1StreamReaderSequence;
049import com.unboundid.ldap.sdk.Control;
050import com.unboundid.ldap.sdk.LDAPException;
051import com.unboundid.ldap.sdk.LDAPResult;
052import com.unboundid.ldap.sdk.ResultCode;
053import com.unboundid.util.Debug;
054import com.unboundid.util.InternalUseOnly;
055import com.unboundid.util.NotExtensible;
056import com.unboundid.util.NotNull;
057import com.unboundid.util.Nullable;
058import com.unboundid.util.StaticUtils;
059import com.unboundid.util.ThreadSafety;
060import com.unboundid.util.ThreadSafetyLevel;
061import com.unboundid.util.Validator;
062
063import static com.unboundid.ldap.protocol.ProtocolMessages.*;
064
065
066
067/**
068 * This class provides an implementation of a generic response protocol op.
069 * It must be subclassed by classes providing implementations for each
070 * operation type.
071 */
072@InternalUseOnly()
073@NotExtensible()
074@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
075public abstract class GenericResponseProtocolOp
076       implements ProtocolOp
077{
078  /**
079   * The BER type for the referral URLs elements.
080   */
081  public static final byte TYPE_REFERRALS = (byte) 0xA3;
082
083
084
085  /**
086   * The serial version UID for this serializable class.
087   */
088  private static final long serialVersionUID = 3837308973105414874L;
089
090
091
092  // The BER type for this response.
093  private final byte type;
094
095  // The result code for this response.
096  private final int resultCode;
097
098  // The referral URLs for this response.
099  @NotNull private final List<String> referralURLs;
100
101  // The diagnostic message for this response.
102  @Nullable private final String diagnosticMessage;
103
104  // The matched DN for this response.Static
105  @Nullable private final String matchedDN;
106
107
108
109  /**
110   * Creates a new instance of this response with the provided information.
111   *
112   * @param  type               The BER type for this response.
113   * @param  resultCode         The result code for this response.
114   * @param  matchedDN          The matched DN for this result, if available.
115   * @param  diagnosticMessage  The diagnostic message for this response, if
116   *                            available.
117   * @param  referralURLs       The list of referral URLs for this response, if
118   *                            available.
119   */
120  protected GenericResponseProtocolOp(final byte type, final int resultCode,
121                                      @Nullable final String matchedDN,
122                                      @Nullable final String diagnosticMessage,
123                                      @Nullable final List<String> referralURLs)
124  {
125    this.type              = type;
126    this.resultCode        = resultCode;
127    this.matchedDN         = matchedDN;
128    this.diagnosticMessage = diagnosticMessage;
129
130    if (referralURLs == null)
131    {
132      this.referralURLs = Collections.emptyList();
133    }
134    else
135    {
136      this.referralURLs = Collections.unmodifiableList(referralURLs);
137    }
138  }
139
140
141
142  /**
143   * Creates a new response read from the provided ASN.1 stream reader.
144   *
145   * @param  reader  The ASN.1 stream reader from which to read the response.
146   *
147   * @throws  LDAPException  If a problem occurs while reading or parsing the
148   *                         response.
149   */
150  protected GenericResponseProtocolOp(@NotNull final ASN1StreamReader reader)
151            throws LDAPException
152  {
153    try
154    {
155      type = (byte) reader.peek();
156      final ASN1StreamReaderSequence opSequence = reader.beginSequence();
157      resultCode = reader.readEnumerated();
158
159      String s = reader.readString();
160      Validator.ensureNotNull(s);
161      if (s.isEmpty())
162      {
163        matchedDN = null;
164      }
165      else
166      {
167        matchedDN = s;
168      }
169
170      s = reader.readString();
171      Validator.ensureNotNull(s);
172      if (s.isEmpty())
173      {
174        diagnosticMessage = null;
175      }
176      else
177      {
178        diagnosticMessage = s;
179      }
180
181      if (opSequence.hasMoreElements())
182      {
183        final ArrayList<String> refs = new ArrayList<>(1);
184        final ASN1StreamReaderSequence refSequence = reader.beginSequence();
185        while (refSequence.hasMoreElements())
186        {
187          refs.add(reader.readString());
188        }
189        referralURLs = Collections.unmodifiableList(refs);
190      }
191      else
192      {
193        referralURLs = Collections.emptyList();
194      }
195    }
196    catch (final Exception e)
197    {
198      Debug.debugException(e);
199      throw new LDAPException(ResultCode.DECODING_ERROR,
200           ERR_RESPONSE_CANNOT_DECODE.get(
201                StaticUtils.getExceptionMessage(e)), e);
202    }
203  }
204
205
206
207  /**
208   * Retrieves the result code for this response.
209   *
210   * @return  The result code for this response.
211   */
212  public final int getResultCode()
213  {
214    return resultCode;
215  }
216
217
218
219  /**
220   * Retrieves the matched DN for this response, if any.
221   *
222   * @return  The matched DN for this response, or {@code null} if there is
223   *          no matched DN.
224   */
225  @Nullable()
226  public final String getMatchedDN()
227  {
228    return matchedDN;
229  }
230
231
232
233  /**
234   * Retrieves the diagnostic message for this response, if any.
235   *
236   * @return  The diagnostic message for this response, or {@code null} if there
237   *          is no diagnostic message.
238   */
239  @Nullable()
240  public final String getDiagnosticMessage()
241  {
242    return diagnosticMessage;
243  }
244
245
246
247  /**
248   * Retrieves the list of referral URLs for this response.
249   *
250   * @return  The list of referral URLs for this response, or an empty list
251   *          if there are no referral URLs.
252   */
253  @NotNull()
254  public final List<String> getReferralURLs()
255  {
256    return referralURLs;
257  }
258
259
260
261  /**
262   * {@inheritDoc}
263   */
264  @Override()
265  public byte getProtocolOpType()
266  {
267    return type;
268  }
269
270
271
272  /**
273   * {@inheritDoc}
274   */
275  @Override()
276  public final void writeTo(@NotNull final ASN1Buffer buffer)
277  {
278    final ASN1BufferSequence opSequence = buffer.beginSequence(type);
279    buffer.addEnumerated(resultCode);
280    buffer.addOctetString(matchedDN);
281    buffer.addOctetString(diagnosticMessage);
282
283    if (! referralURLs.isEmpty())
284    {
285      final ASN1BufferSequence refSequence =
286           buffer.beginSequence(TYPE_REFERRALS);
287      for (final String s : referralURLs)
288      {
289        buffer.addOctetString(s);
290      }
291      refSequence.end();
292    }
293    opSequence.end();
294  }
295
296
297
298  /**
299   * Creates a new LDAP result object from this response protocol op.
300   *
301   * @param  controls  The set of controls to include in the LDAP result.  It
302   *                   may be empty or {@code null} if no controls should be
303   *                   included.
304   *
305   * @return  The LDAP result that was created.
306   */
307  @NotNull()
308  public LDAPResult toLDAPResult(@Nullable final Control... controls)
309  {
310    final String[] refs;
311    if (referralURLs.isEmpty())
312    {
313      refs = StaticUtils.NO_STRINGS;
314    }
315    else
316    {
317      refs = new String[referralURLs.size()];
318      referralURLs.toArray(refs);
319    }
320
321    return new LDAPResult(-1, ResultCode.valueOf(resultCode), diagnosticMessage,
322         matchedDN, refs, controls);
323  }
324
325
326
327  /**
328   * Retrieves a string representation of this protocol op.
329   *
330   * @return  A string representation of this protocol op.
331   */
332  @Override()
333  @NotNull()
334  public final String toString()
335  {
336    final StringBuilder buffer = new StringBuilder();
337    toString(buffer);
338    return buffer.toString();
339  }
340
341
342
343  /**
344   * {@inheritDoc}
345   */
346  @Override()
347  public final void toString(@NotNull final StringBuilder buffer)
348  {
349    buffer.append("ResponseProtocolOp(type=");
350    StaticUtils.toHex(type, buffer);
351    buffer.append(", resultCode=");
352    buffer.append(resultCode);
353
354    if (matchedDN != null)
355    {
356      buffer.append(", matchedDN='");
357      buffer.append(matchedDN);
358      buffer.append('\'');
359    }
360
361    if (diagnosticMessage != null)
362    {
363      buffer.append(", diagnosticMessage='");
364      buffer.append(diagnosticMessage);
365      buffer.append('\'');
366    }
367
368    if (! referralURLs.isEmpty())
369    {
370      buffer.append(", referralURLs={");
371
372      final Iterator<String> iterator = referralURLs.iterator();
373      while (iterator.hasNext())
374      {
375        buffer.append('\'');
376        buffer.append(iterator.next());
377        buffer.append('\'');
378        if (iterator.hasNext())
379        {
380          buffer.append(',');
381        }
382      }
383
384      buffer.append('}');
385    }
386    buffer.append(')');
387  }
388}