001/*
002 * Copyright 2007-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2007-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) 2007-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.sdk;
037
038
039
040import java.io.Serializable;
041import java.util.ArrayList;
042
043import com.unboundid.asn1.ASN1StreamReader;
044import com.unboundid.asn1.ASN1StreamReaderSequence;
045import com.unboundid.ldap.protocol.LDAPResponse;
046import com.unboundid.util.Debug;
047import com.unboundid.util.NotMutable;
048import com.unboundid.util.NotNull;
049import com.unboundid.util.Nullable;
050import com.unboundid.util.StaticUtils;
051import com.unboundid.util.ThreadSafety;
052import com.unboundid.util.ThreadSafetyLevel;
053import com.unboundid.util.Validator;
054
055import static com.unboundid.ldap.sdk.LDAPMessages.*;
056
057
058
059/**
060 * This class provides a data structure for representing an LDAP search result
061 * reference.  A search result reference consists of a set of referral URLs and
062 * may also include zero or more controls.  It describes an alternate location
063 * in which additional results for the search may be found.  If there are
064 * multiple referral URLs, then they should all be considered equivalent ways
065 * to access the information (e.g., referrals referencing different servers that
066 * may be contacted).
067 */
068@NotMutable()
069@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
070public final class SearchResultReference
071       implements Serializable, LDAPResponse
072{
073  /**
074   * The serial version UID for this serializable class.
075   */
076  private static final long serialVersionUID = 5675961266319346053L;
077
078
079
080  // The set of controls returned with this search result reference.
081  @NotNull private final Control[] controls;
082
083  // The message ID for the LDAP message containing this response.
084  private final int messageID;
085
086  // The set of referral URLs for this search result reference.
087  @NotNull private final String[] referralURLs;
088
089
090
091  /**
092   * Creates a new search result reference with the provided information.
093   *
094   * @param  referralURLs  The set of referral URLs for this search result
095   *                       reference.  It must not be {@code null}.
096   * @param  controls      The set of controls returned with this search result
097   *                       reference.  It must not be {@code null}.
098   */
099  public SearchResultReference(@NotNull final String[] referralURLs,
100                               @NotNull final Control[] controls)
101  {
102    this(-1, referralURLs, controls);
103  }
104
105
106
107  /**
108   * Creates a new search result reference with the provided information.
109   *
110   * @param  messageID     The message ID for the LDAP message containing this
111   *                       response.
112   * @param  referralURLs  The set of referral URLs for this search result
113   *                       reference.  It must not be {@code null}.
114   * @param  controls      The set of controls returned with this search result
115   *                       reference.  It must not be {@code null}.
116   */
117  public SearchResultReference(final int messageID,
118                               @NotNull final String[] referralURLs,
119                               @NotNull final Control[] controls)
120  {
121    Validator.ensureNotNull(referralURLs);
122
123    this.messageID    = messageID;
124    this.referralURLs = referralURLs;
125
126    if (controls == null)
127    {
128      this.controls = NO_CONTROLS;
129    }
130    else
131    {
132      this.controls = controls;
133    }
134  }
135
136
137
138  /**
139   * Creates a new search result reference object with the protocol op and
140   * controls read from the given ASN.1 stream reader.
141   *
142   * @param  messageID        The message ID for the LDAP message containing
143   *                          this response.
144   * @param  messageSequence  The ASN.1 stream reader sequence used in the
145   *                          course of reading the LDAP message elements.
146   * @param  reader           The ASN.1 stream reader from which to read the
147   *                          protocol op and controls.
148   *
149   * @return  The decoded search result reference object.
150   *
151   * @throws  LDAPException  If a problem occurs while reading or decoding data
152   *                         from the ASN.1 stream reader.
153   */
154  @NotNull()
155  static SearchResultReference readSearchReferenceFrom(final int messageID,
156              @NotNull final ASN1StreamReaderSequence messageSequence,
157              @NotNull final ASN1StreamReader reader)
158         throws LDAPException
159  {
160    try
161    {
162      final ArrayList<String> refList = new ArrayList<>(5);
163      final ASN1StreamReaderSequence refSequence = reader.beginSequence();
164      while (refSequence.hasMoreElements())
165      {
166        refList.add(reader.readString());
167      }
168
169      final String[] referralURLs = new String[refList.size()];
170      refList.toArray(referralURLs);
171
172      Control[] controls = NO_CONTROLS;
173      if (messageSequence.hasMoreElements())
174      {
175        final ArrayList<Control> controlList = new ArrayList<>(5);
176        final ASN1StreamReaderSequence controlSequence = reader.beginSequence();
177        while (controlSequence.hasMoreElements())
178        {
179          controlList.add(Control.readFrom(reader));
180        }
181
182        controls = new Control[controlList.size()];
183        controlList.toArray(controls);
184      }
185
186      return new SearchResultReference(messageID, referralURLs, controls);
187    }
188    catch (final LDAPException le)
189    {
190      Debug.debugException(le);
191      throw le;
192    }
193    catch (final Exception e)
194    {
195      Debug.debugException(e);
196      throw new LDAPException(ResultCode.DECODING_ERROR,
197           ERR_SEARCH_REFERENCE_CANNOT_DECODE.get(
198                StaticUtils.getExceptionMessage(e)),
199           e);
200    }
201  }
202
203
204
205  /**
206   * {@inheritDoc}
207   */
208  @Override()
209  public int getMessageID()
210  {
211    return messageID;
212  }
213
214
215
216  /**
217   * Retrieves the set of referral URLs for this search result reference.
218   *
219   * @return  The set of referral URLs for this search result reference.
220   */
221  @NotNull()
222  public String[] getReferralURLs()
223  {
224    return referralURLs;
225  }
226
227
228
229  /**
230   * Retrieves the set of controls returned with this search result reference.
231   * Individual response controls of a specific type may be retrieved and
232   * decoded using the {@code get} method in the response control class.
233   *
234   * @return  The set of controls returned with this search result reference.
235   */
236  @NotNull()
237  public Control[] getControls()
238  {
239    return controls;
240  }
241
242
243
244  /**
245   * Retrieves the control with the specified OID.  If there is more than one
246   * control with the given OID, then the first will be returned.
247   *
248   * @param  oid  The OID of the control to retrieve.
249   *
250   * @return  The control with the requested OID, or {@code null} if there is no
251   *          such control for this search result reference.
252   */
253  @Nullable()
254  public Control getControl(@NotNull final String oid)
255  {
256    for (final Control c : controls)
257    {
258      if (c.getOID().equals(oid))
259      {
260        return c;
261      }
262    }
263
264    return null;
265  }
266
267
268
269  /**
270   * Retrieves a string representation of this search result reference.
271   *
272   * @return  A string representation of this search result reference.
273   */
274  @Override()
275  @NotNull()
276  public String toString()
277  {
278    final StringBuilder buffer = new StringBuilder();
279    toString(buffer);
280    return buffer.toString();
281  }
282
283
284
285  /**
286   * Appends a string representation of this search result reference to the
287   * provided buffer.
288   *
289   * @param  buffer  The buffer to which to append the string representation of
290   *                 this search result reference.
291   */
292  @Override()
293  public void toString(@NotNull final StringBuilder buffer)
294  {
295    buffer.append("SearchResultReference(referralURLs={");
296    for (int i=0; i < referralURLs.length; i++)
297    {
298      if (i > 0)
299      {
300        buffer.append(", ");
301      }
302      buffer.append(referralURLs[i]);
303    }
304    buffer.append('}');
305
306    if (messageID >= 0)
307    {
308      buffer.append(", messageID=");
309      buffer.append(messageID);
310    }
311
312    buffer.append(", controls={");
313
314    for (int i=0; i < controls.length; i++)
315    {
316      if (i > 0)
317      {
318        buffer.append(", ");
319      }
320
321      controls[i].toString(buffer);
322    }
323
324    buffer.append("})");
325  }
326}