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