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