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.util.ArrayList; 026 027import com.unboundid.asn1.ASN1OctetString; 028import com.unboundid.asn1.ASN1StreamReader; 029import com.unboundid.asn1.ASN1StreamReaderSequence; 030import com.unboundid.util.Debug; 031import com.unboundid.util.Extensible; 032import com.unboundid.util.NotMutable; 033import com.unboundid.util.StaticUtils; 034import com.unboundid.util.ThreadSafety; 035import com.unboundid.util.ThreadSafetyLevel; 036 037import static com.unboundid.ldap.sdk.LDAPMessages.*; 038 039 040 041/** 042 * This class provides a data structure for holding information about the result 043 * of processing an extended operation. It includes all of the generic LDAP 044 * result elements as described in the {@link LDAPResult} class, but it may also 045 * include the following elements: 046 * <UL> 047 * <LI>Response OID -- An optional OID that can be used to identify the type 048 * of response. This may be used if there can be different types of 049 * responses for a given request.</LI> 050 * <LI>Value -- An optional element that provides the encoded value for this 051 * response. If a value is provided, then the encoding for the value 052 * depends on the type of extended result.</LI> 053 * </UL> 054 */ 055@Extensible() 056@NotMutable() 057@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 058public class ExtendedResult 059 extends LDAPResult 060{ 061 /** 062 * The BER type for the extended response OID element. 063 */ 064 private static final byte TYPE_EXTENDED_RESPONSE_OID = (byte) 0x8A; 065 066 067 068 /** 069 * The BER type for the extended response value element. 070 */ 071 private static final byte TYPE_EXTENDED_RESPONSE_VALUE = (byte) 0x8B; 072 073 074 075 /** 076 * The serial version UID for this serializable class. 077 */ 078 private static final long serialVersionUID = -6885923482396647963L; 079 080 081 082 // The encoded value for this extended response, if available. 083 private final ASN1OctetString value; 084 085 // The OID for this extended response, if available. 086 private final String oid; 087 088 089 090 /** 091 * Creates a new extended result with the provided information. 092 * 093 * @param messageID The message ID for the LDAP message that is 094 * associated with this LDAP result. 095 * @param resultCode The result code from the response. 096 * @param diagnosticMessage The diagnostic message from the response, if 097 * available. 098 * @param matchedDN The matched DN from the response, if available. 099 * @param referralURLs The set of referral URLs from the response, if 100 * available. 101 * @param oid The OID for this extended response, if 102 * available. 103 * @param value The encoded value for this extended response, if 104 * available. 105 * @param responseControls The set of controls from the response, if 106 * available. 107 */ 108 public ExtendedResult(final int messageID, final ResultCode resultCode, 109 final String diagnosticMessage, final String matchedDN, 110 final String[] referralURLs, final String oid, 111 final ASN1OctetString value, 112 final Control[] responseControls) 113 { 114 super(messageID, resultCode, diagnosticMessage, matchedDN, referralURLs, 115 responseControls); 116 117 this.oid = oid; 118 this.value = value; 119 } 120 121 122 123 /** 124 * Creates a new extended result with the information contained in the 125 * provided LDAP result. The extended result will not have an OID or value. 126 * 127 * @param result The LDAP result whose content should be used for this 128 * extended result. 129 */ 130 public ExtendedResult(final LDAPResult result) 131 { 132 super(result); 133 134 oid = null; 135 value = null; 136 } 137 138 139 140 /** 141 * Creates a new extended result from the provided {@code LDAPException}. 142 * The extended result will not have an OID or value. 143 * 144 * @param exception The {@code LDAPException} to use to create this extended 145 * result. 146 */ 147 public ExtendedResult(final LDAPException exception) 148 { 149 this(exception.toLDAPResult()); 150 } 151 152 153 154 /** 155 * Creates a new extended result initialized from all of the elements of the 156 * provided extended response. 157 * 158 * @param extendedResult The extended response to use to initialize this 159 * extended response. 160 */ 161 protected ExtendedResult(final ExtendedResult extendedResult) 162 { 163 this(extendedResult.getMessageID(), extendedResult.getResultCode(), 164 extendedResult.getDiagnosticMessage(), extendedResult.getMatchedDN(), 165 extendedResult.getReferralURLs(), extendedResult.getOID(), 166 extendedResult.getValue(), extendedResult.getResponseControls()); 167 } 168 169 170 171 /** 172 * Creates a new extended result object with the provided message ID and with 173 * the protocol op and controls read from the given ASN.1 stream reader. 174 * 175 * @param messageID The LDAP message ID for the LDAP message that is 176 * associated with this extended result. 177 * @param messageSequence The ASN.1 stream reader sequence used in the 178 * course of reading the LDAP message elements. 179 * @param reader The ASN.1 stream reader from which to read the 180 * protocol op and controls. 181 * 182 * @return The decoded extended result. 183 * 184 * @throws LDAPException If a problem occurs while reading or decoding data 185 * from the ASN.1 stream reader. 186 */ 187 static ExtendedResult readExtendedResultFrom(final int messageID, 188 final ASN1StreamReaderSequence messageSequence, 189 final ASN1StreamReader reader) 190 throws LDAPException 191 { 192 try 193 { 194 final ASN1StreamReaderSequence protocolOpSequence = 195 reader.beginSequence(); 196 final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated()); 197 198 String matchedDN = reader.readString(); 199 if (matchedDN.length() == 0) 200 { 201 matchedDN = null; 202 } 203 204 String diagnosticMessage = reader.readString(); 205 if (diagnosticMessage.length() == 0) 206 { 207 diagnosticMessage = null; 208 } 209 210 String[] referralURLs = null; 211 String oid = null; 212 ASN1OctetString value = null; 213 while (protocolOpSequence.hasMoreElements()) 214 { 215 final byte type = (byte) reader.peek(); 216 switch (type) 217 { 218 case TYPE_REFERRAL_URLS: 219 final ArrayList<String> refList = new ArrayList<>(1); 220 final ASN1StreamReaderSequence refSequence = reader.beginSequence(); 221 while (refSequence.hasMoreElements()) 222 { 223 refList.add(reader.readString()); 224 } 225 referralURLs = new String[refList.size()]; 226 refList.toArray(referralURLs); 227 break; 228 229 case TYPE_EXTENDED_RESPONSE_OID: 230 oid = reader.readString(); 231 break; 232 233 case TYPE_EXTENDED_RESPONSE_VALUE: 234 value = new ASN1OctetString(type, reader.readBytes()); 235 break; 236 237 default: 238 throw new LDAPException(ResultCode.DECODING_ERROR, 239 ERR_EXTENDED_RESULT_INVALID_ELEMENT.get( 240 StaticUtils.toHex(type))); 241 } 242 } 243 244 Control[] controls = NO_CONTROLS; 245 if (messageSequence.hasMoreElements()) 246 { 247 final ArrayList<Control> controlList = new ArrayList<>(1); 248 final ASN1StreamReaderSequence controlSequence = reader.beginSequence(); 249 while (controlSequence.hasMoreElements()) 250 { 251 controlList.add(Control.readFrom(reader)); 252 } 253 254 controls = new Control[controlList.size()]; 255 controlList.toArray(controls); 256 } 257 258 return new ExtendedResult(messageID, resultCode, diagnosticMessage, 259 matchedDN, referralURLs, oid, value, controls); 260 } 261 catch (final LDAPException le) 262 { 263 Debug.debugException(le); 264 throw le; 265 } 266 catch (final Exception e) 267 { 268 Debug.debugException(e); 269 throw new LDAPException(ResultCode.DECODING_ERROR, 270 ERR_EXTENDED_RESULT_CANNOT_DECODE.get( 271 StaticUtils.getExceptionMessage(e)), 272 e); 273 } 274 } 275 276 277 278 /** 279 * Retrieves the OID for this extended result, if available. 280 * 281 * @return The OID for this extended result, or {@code null} if none is 282 * available. 283 */ 284 public final String getOID() 285 { 286 return oid; 287 } 288 289 290 291 /** 292 * Indicates whether this extended result has a value. 293 * 294 * @return {@code true} if this extended result has a value, or 295 * {@code false} if not. 296 */ 297 public final boolean hasValue() 298 { 299 return (value != null); 300 } 301 302 303 304 /** 305 * Retrieves the encoded value for this extended result, if available. 306 * 307 * @return The encoded value for this extended result, or {@code null} if 308 * none is available. 309 */ 310 public final ASN1OctetString getValue() 311 { 312 return value; 313 } 314 315 316 317 /** 318 * Retrieves the user-friendly name for the extended result, if available. 319 * If no user-friendly name has been defined, but a response OID is available, 320 * then that will be returned. If neither a user-friendly name nor a response 321 * OID are available, then {@code null} will be returned. 322 * 323 * @return The user-friendly name for this extended request, the response OID 324 * if a user-friendly name is not available but a response OID is, or 325 * {@code null} if neither a user-friendly name nor a response OID 326 * are available. 327 */ 328 public String getExtendedResultName() 329 { 330 // By default, we will return the OID (which may be null). Subclasses 331 // should override this to provide the user-friendly name. 332 return oid; 333 } 334 335 336 337 /** 338 * Retrieves a string representation of this extended response. 339 * 340 * @return A string representation of this extended response. 341 */ 342 @Override() 343 public String toString() 344 { 345 final StringBuilder buffer = new StringBuilder(); 346 toString(buffer); 347 return buffer.toString(); 348 } 349 350 351 352 /** 353 * Appends a string representation of this extended response to the provided 354 * buffer. 355 * 356 * @param buffer The buffer to which a string representation of this 357 * extended response will be appended. 358 */ 359 @Override() 360 public void toString(final StringBuilder buffer) 361 { 362 buffer.append("ExtendedResult(resultCode="); 363 buffer.append(getResultCode()); 364 365 final int messageID = getMessageID(); 366 if (messageID >= 0) 367 { 368 buffer.append(", messageID="); 369 buffer.append(messageID); 370 } 371 372 final String diagnosticMessage = getDiagnosticMessage(); 373 if (diagnosticMessage != null) 374 { 375 buffer.append(", diagnosticMessage='"); 376 buffer.append(diagnosticMessage); 377 buffer.append('\''); 378 } 379 380 final String matchedDN = getMatchedDN(); 381 if (matchedDN != null) 382 { 383 buffer.append(", matchedDN='"); 384 buffer.append(matchedDN); 385 buffer.append('\''); 386 } 387 388 final String[] referralURLs = getReferralURLs(); 389 if (referralURLs.length > 0) 390 { 391 buffer.append(", referralURLs={"); 392 for (int i=0; i < referralURLs.length; i++) 393 { 394 if (i > 0) 395 { 396 buffer.append(", "); 397 } 398 399 buffer.append(referralURLs[i]); 400 } 401 buffer.append('}'); 402 } 403 404 if (oid != null) 405 { 406 buffer.append(", oid="); 407 buffer.append(oid); 408 } 409 410 final Control[] responseControls = getResponseControls(); 411 if (responseControls.length > 0) 412 { 413 buffer.append(", responseControls={"); 414 for (int i=0; i < responseControls.length; i++) 415 { 416 if (i > 0) 417 { 418 buffer.append(", "); 419 } 420 421 buffer.append(responseControls[i]); 422 } 423 buffer.append('}'); 424 } 425 426 buffer.append(')'); 427 } 428}