001/* 002 * Copyright 2007-2018 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2018 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.NotMutable; 032import com.unboundid.util.ThreadSafety; 033import com.unboundid.util.ThreadSafetyLevel; 034 035import static com.unboundid.ldap.sdk.LDAPMessages.*; 036import static com.unboundid.util.Debug.*; 037import static com.unboundid.util.StaticUtils.*; 038import static com.unboundid.util.Validator.*; 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 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<String>(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<Control>(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 debugException(le); 172 throw le; 173 } 174 catch (final Exception e) 175 { 176 debugException(e); 177 throw new LDAPException(ResultCode.DECODING_ERROR, 178 ERR_SEARCH_REFERENCE_CANNOT_DECODE.get(getExceptionMessage(e)), e); 179 } 180 } 181 182 183 184 /** 185 * {@inheritDoc} 186 */ 187 @Override() 188 public int getMessageID() 189 { 190 return messageID; 191 } 192 193 194 195 /** 196 * Retrieves the set of referral URLs for this search result reference. 197 * 198 * @return The set of referral URLs for this search result reference. 199 */ 200 public String[] getReferralURLs() 201 { 202 return referralURLs; 203 } 204 205 206 207 /** 208 * Retrieves the set of controls returned with this search result reference. 209 * Individual response controls of a specific type may be retrieved and 210 * decoded using the {@code get} method in the response control class. 211 * 212 * @return The set of controls returned with this search result reference. 213 */ 214 public Control[] getControls() 215 { 216 return controls; 217 } 218 219 220 221 /** 222 * Retrieves the control with the specified OID. If there is more than one 223 * control with the given OID, then the first will be returned. 224 * 225 * @param oid The OID of the control to retrieve. 226 * 227 * @return The control with the requested OID, or {@code null} if there is no 228 * such control for this search result reference. 229 */ 230 public Control getControl(final String oid) 231 { 232 for (final Control c : controls) 233 { 234 if (c.getOID().equals(oid)) 235 { 236 return c; 237 } 238 } 239 240 return null; 241 } 242 243 244 245 /** 246 * Retrieves a string representation of this search result reference. 247 * 248 * @return A string representation of this search result reference. 249 */ 250 @Override() 251 public String toString() 252 { 253 final StringBuilder buffer = new StringBuilder(); 254 toString(buffer); 255 return buffer.toString(); 256 } 257 258 259 260 /** 261 * Appends a string representation of this search result reference to the 262 * provided buffer. 263 * 264 * @param buffer The buffer to which to append the string representation of 265 * this search result reference. 266 */ 267 public void toString(final StringBuilder buffer) 268 { 269 buffer.append("SearchResultReference(referralURLs={"); 270 for (int i=0; i < referralURLs.length; i++) 271 { 272 if (i > 0) 273 { 274 buffer.append(", "); 275 } 276 buffer.append(referralURLs[i]); 277 } 278 buffer.append('}'); 279 280 if (messageID >= 0) 281 { 282 buffer.append(", messageID="); 283 buffer.append(messageID); 284 } 285 286 buffer.append(", controls={"); 287 288 for (int i=0; i < controls.length; i++) 289 { 290 if (i > 0) 291 { 292 buffer.append(", "); 293 } 294 295 controls[i].toString(buffer); 296 } 297 298 buffer.append("})"); 299 } 300}