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