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}