001 /* 002 * Copyright 2012-2015 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005 /* 006 * Copyright (C) 2015 UnboundID Corp. 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 */ 021 package com.unboundid.ldap.sdk.unboundidds.extensions; 022 023 024 025 import java.util.ArrayList; 026 import java.util.Collections; 027 import java.util.Iterator; 028 import java.util.List; 029 030 import com.unboundid.asn1.ASN1Element; 031 import com.unboundid.asn1.ASN1Enumerated; 032 import com.unboundid.asn1.ASN1OctetString; 033 import com.unboundid.asn1.ASN1Sequence; 034 import com.unboundid.ldap.protocol.AddResponseProtocolOp; 035 import com.unboundid.ldap.protocol.DeleteResponseProtocolOp; 036 import com.unboundid.ldap.protocol.ExtendedResponseProtocolOp; 037 import com.unboundid.ldap.protocol.LDAPMessage; 038 import com.unboundid.ldap.protocol.ModifyDNResponseProtocolOp; 039 import com.unboundid.ldap.protocol.ModifyResponseProtocolOp; 040 import com.unboundid.ldap.sdk.Control; 041 import com.unboundid.ldap.sdk.ExtendedResult; 042 import com.unboundid.ldap.sdk.LDAPException; 043 import com.unboundid.ldap.sdk.LDAPResult; 044 import com.unboundid.ldap.sdk.OperationType; 045 import com.unboundid.ldap.sdk.ResultCode; 046 import com.unboundid.util.Debug; 047 import com.unboundid.util.NotMutable; 048 import com.unboundid.util.ObjectPair; 049 import com.unboundid.util.StaticUtils; 050 import com.unboundid.util.ThreadSafety; 051 import com.unboundid.util.ThreadSafetyLevel; 052 053 import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*; 054 055 056 057 /** 058 * <BLOCKQUOTE> 059 * <B>NOTE:</B> This class is part of the Commercial Edition of the UnboundID 060 * LDAP SDK for Java. It is not available for use in applications that 061 * include only the Standard Edition of the LDAP SDK, and is not supported for 062 * use in conjunction with non-UnboundID products. 063 * </BLOCKQUOTE> 064 * This class provides an implementation of an extended result that can be used 065 * to provide information about the processing for a 066 * {@link MultiUpdateExtendedRequest}. The OID for this result is 067 * 1.3.6.1.4.1.30221.2.6.18, and the value (if present) should have the 068 * following encoding: 069 * <BR><BR> 070 * <PRE> 071 * MultiUpdateResultValue ::= SEQUENCE { 072 * changesApplied ENUMERATED { 073 * none (0), 074 * all (1), 075 * partial (2), 076 * ... }, 077 * responses SEQUENCE OF SEQUENCE { 078 * responseOp CHOICE { 079 * modifyResponse ModifyResponse, 080 * addResponse AddResponse, 081 * delResponse DelResponse, 082 * modDNResponse ModifyDNResponse, 083 * extendedResp ExtendedResponse, 084 * ... }, 085 * controls [0] Controls OPTIONAL, 086 * ... }, 087 * } 088 * </PRE> 089 * 090 * @see MultiUpdateChangesApplied 091 * @see MultiUpdateExtendedRequest 092 */ 093 @NotMutable() 094 @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 095 public final class MultiUpdateExtendedResult 096 extends ExtendedResult 097 { 098 /** 099 * The OID (1.3.6.1.4.1.30221.2.6.18) for the multi-update extended result. 100 */ 101 public static final String MULTI_UPDATE_RESULT_OID = 102 "1.3.6.1.4.1.30221.2.6.18"; 103 104 105 106 /** 107 * The serial version UID for this serializable class. 108 */ 109 private static final long serialVersionUID = -2529988892013489969L; 110 111 112 113 // The set of results for the operations that were processed. 114 private final List<ObjectPair<OperationType,LDAPResult>> results; 115 116 // The changes applied value for this result. 117 private final MultiUpdateChangesApplied changesApplied; 118 119 120 121 /** 122 * Creates a new multi-update extended result from the provided extended 123 * result. 124 * 125 * @param extendedResult The extended result to be decoded as a multi-update 126 * result. 127 * 128 * @throws LDAPException If a problem is encountered while attempting to 129 * decode the provided extended result as a 130 * multi-update result. 131 */ 132 public MultiUpdateExtendedResult(final ExtendedResult extendedResult) 133 throws LDAPException 134 { 135 super(extendedResult); 136 137 final ASN1OctetString value = extendedResult.getValue(); 138 if (value == null) 139 { 140 changesApplied = MultiUpdateChangesApplied.NONE; 141 results = Collections.emptyList(); 142 return; 143 } 144 145 try 146 { 147 final ASN1Element[] outerSequenceElements = 148 ASN1Sequence.decodeAsSequence(value.getValue()).elements(); 149 150 final int cav = ASN1Enumerated.decodeAsEnumerated( 151 outerSequenceElements[0]).intValue(); 152 changesApplied = MultiUpdateChangesApplied.valueOf(cav); 153 if (changesApplied == null) 154 { 155 throw new LDAPException(ResultCode.DECODING_ERROR, 156 ERR_MULTI_UPDATE_RESULT_INVALID_CHANGES_APPLIED.get(cav)); 157 } 158 159 final ASN1Element[] responseSetElements = 160 ASN1Sequence.decodeAsSequence(outerSequenceElements[1]).elements(); 161 final ArrayList<ObjectPair<OperationType,LDAPResult>> rl = 162 new ArrayList<ObjectPair<OperationType,LDAPResult>>( 163 responseSetElements.length); 164 for (final ASN1Element rse : responseSetElements) 165 { 166 final ASN1Element[] elements = 167 ASN1Sequence.decodeAsSequence(rse).elements(); 168 final Control[] controls; 169 if (elements.length == 2) 170 { 171 controls = Control.decodeControls( 172 ASN1Sequence.decodeAsSequence(elements[1])); 173 } 174 else 175 { 176 controls = null; 177 } 178 179 switch (elements[0].getType()) 180 { 181 case LDAPMessage.PROTOCOL_OP_TYPE_ADD_RESPONSE: 182 rl.add(new ObjectPair<OperationType,LDAPResult>(OperationType.ADD, 183 AddResponseProtocolOp.decodeProtocolOp(elements[0]). 184 toLDAPResult(controls))); 185 break; 186 case LDAPMessage.PROTOCOL_OP_TYPE_DELETE_RESPONSE: 187 rl.add(new ObjectPair<OperationType,LDAPResult>( 188 OperationType.DELETE, 189 DeleteResponseProtocolOp.decodeProtocolOp(elements[0]). 190 toLDAPResult(controls))); 191 break; 192 case LDAPMessage.PROTOCOL_OP_TYPE_EXTENDED_RESPONSE: 193 rl.add(new ObjectPair<OperationType,LDAPResult>( 194 OperationType.EXTENDED, 195 ExtendedResponseProtocolOp.decodeProtocolOp(elements[0]). 196 toExtendedResult(controls))); 197 break; 198 case LDAPMessage.PROTOCOL_OP_TYPE_MODIFY_RESPONSE: 199 rl.add(new ObjectPair<OperationType,LDAPResult>( 200 OperationType.MODIFY, 201 ModifyResponseProtocolOp.decodeProtocolOp(elements[0]). 202 toLDAPResult(controls))); 203 break; 204 case LDAPMessage.PROTOCOL_OP_TYPE_MODIFY_DN_RESPONSE: 205 rl.add(new ObjectPair<OperationType,LDAPResult>( 206 OperationType.MODIFY_DN, 207 ModifyDNResponseProtocolOp.decodeProtocolOp(elements[0]). 208 toLDAPResult(controls))); 209 break; 210 default: 211 throw new LDAPException(ResultCode.DECODING_ERROR, 212 ERR_MULTI_UPDATE_RESULT_DECODE_INVALID_OP_TYPE.get( 213 StaticUtils.toHex(elements[0].getType()))); 214 } 215 } 216 217 results = Collections.unmodifiableList(rl); 218 } 219 catch (final LDAPException le) 220 { 221 Debug.debugException(le); 222 throw le; 223 } 224 catch (final Exception e) 225 { 226 Debug.debugException(e); 227 throw new LDAPException(ResultCode.DECODING_ERROR, 228 ERR_MULTI_UPDATE_RESULT_CANNOT_DECODE_VALUE.get( 229 StaticUtils.getExceptionMessage(e)), 230 e); 231 } 232 } 233 234 235 236 /** 237 * Creates a new multi-update extended request with the provided information. 238 * 239 * @param messageID The message ID for this extended result. 240 * @param resultCode The result code for this result. It must not be 241 * {@code null}. 242 * @param diagnosticMessage The diagnostic message to include in the result. 243 * It may be {@code null} if no diagnostic message 244 * should be included. 245 * @param matchedDN The matched DN to include in the result. It may 246 * be {@code null} if no matched DN should be 247 * included. 248 * @param referralURLs The set of referral URLs to include in the 249 * result. It may be {@code null} or empty if no 250 * referral URLs should be included. 251 * @param changesApplied The value which indicates whether any or all of 252 * the changes from the request were successfully 253 * applied. 254 * @param results The set of operation results to be included in 255 * the extended result value. It may be 256 * {@code null} or empty if no operation results 257 * should be included. 258 * @param controls The set of controls to include in the 259 * multi-update result. It may be {@code null} or 260 * empty if no controls should be included. 261 * 262 * @throws LDAPException If any of the results are for an inappropriate 263 * operation type. 264 */ 265 public MultiUpdateExtendedResult(final int messageID, 266 final ResultCode resultCode, final String diagnosticMessage, 267 final String matchedDN, final String[] referralURLs, 268 final MultiUpdateChangesApplied changesApplied, 269 final List<ObjectPair<OperationType,LDAPResult>> results, 270 final Control... controls) 271 throws LDAPException 272 { 273 super(messageID, resultCode, diagnosticMessage, matchedDN, referralURLs, 274 MULTI_UPDATE_RESULT_OID, encodeValue(changesApplied, results), 275 controls); 276 277 this.changesApplied = changesApplied; 278 279 if (results == null) 280 { 281 this.results = Collections.emptyList(); 282 } 283 else 284 { 285 this.results = Collections.unmodifiableList(results); 286 } 287 } 288 289 290 291 /** 292 * Encodes the information from the provided set of results into a form 293 * suitable for use as the value of a multi-update extended result. 294 * 295 * @param changesApplied The value which indicates whether any or all of the 296 * changes from the request were successfully applied. 297 * @param results The set of operation results to be included in the 298 * extended result value. It may be {@code null} or 299 * empty if no operation results should be included. 300 * 301 * @return An ASN.1 element suitable for use as the value of a multi-update 302 * extended result. 303 * 304 * @throws LDAPException If any of the results are for an inappropriate 305 * operation type. 306 */ 307 private static ASN1OctetString encodeValue( 308 final MultiUpdateChangesApplied changesApplied, 309 final List<ObjectPair<OperationType,LDAPResult>> results) 310 throws LDAPException 311 { 312 if ((results == null) || results.isEmpty()) 313 { 314 return null; 315 } 316 317 final ArrayList<ASN1Element> opElements = 318 new ArrayList<ASN1Element>(results.size()); 319 for (final ObjectPair<OperationType,LDAPResult> p : results) 320 { 321 final OperationType t = p.getFirst(); 322 final LDAPResult r = p.getSecond(); 323 324 final ASN1Element protocolOpElement; 325 switch (t) 326 { 327 case ADD: 328 protocolOpElement = new AddResponseProtocolOp(r).encodeProtocolOp(); 329 break; 330 case DELETE: 331 protocolOpElement = 332 new DeleteResponseProtocolOp(r).encodeProtocolOp(); 333 break; 334 case EXTENDED: 335 protocolOpElement = 336 new ExtendedResponseProtocolOp(r).encodeProtocolOp(); 337 break; 338 case MODIFY: 339 protocolOpElement = 340 new ModifyResponseProtocolOp(r).encodeProtocolOp(); 341 break; 342 case MODIFY_DN: 343 protocolOpElement = 344 new ModifyDNResponseProtocolOp(r).encodeProtocolOp(); 345 break; 346 default: 347 throw new LDAPException(ResultCode.PARAM_ERROR, 348 ERR_MULTI_UPDATE_RESULT_INVALID_OP_TYPE.get(t.name())); 349 } 350 351 final Control[] controls = r.getResponseControls(); 352 if ((controls == null) || (controls.length == 0)) 353 { 354 opElements.add(new ASN1Sequence(protocolOpElement)); 355 } 356 else 357 { 358 opElements.add(new ASN1Sequence( 359 protocolOpElement, 360 Control.encodeControls(controls))); 361 362 } 363 } 364 365 final ASN1Sequence valueSequence = new ASN1Sequence( 366 new ASN1Enumerated(changesApplied.intValue()), 367 new ASN1Sequence(opElements)); 368 return new ASN1OctetString(valueSequence.encode()); 369 } 370 371 372 373 /** 374 * Retrieves the value that indicates whether any or all changes from the 375 * multi-update request were successfully applied. 376 * 377 * @return The value that indicates whether any or all changes from the 378 * multi-update request were successfully applied. 379 */ 380 public MultiUpdateChangesApplied getChangesApplied() 381 { 382 return changesApplied; 383 } 384 385 386 387 /** 388 * Retrieves a list of the results for operations processed as part of the 389 * multi-update operation, with each result paired with its corresponding 390 * operation type. 391 * 392 * @return A list of the results for operations processed as part of the 393 * multi-update operation. The returned list may be empty if no 394 * operation results were available. 395 */ 396 public List<ObjectPair<OperationType,LDAPResult>> getResults() 397 { 398 return results; 399 } 400 401 402 403 /** 404 * {@inheritDoc} 405 */ 406 @Override() 407 public String getExtendedResultName() 408 { 409 return INFO_EXTENDED_RESULT_NAME_MULTI_UPDATE.get(); 410 } 411 412 413 414 /** 415 * Appends a string representation of this extended result to the provided 416 * buffer. 417 * 418 * @param buffer The buffer to which a string representation of this 419 * extended result will be appended. 420 */ 421 @Override() 422 public void toString(final StringBuilder buffer) 423 { 424 buffer.append("MultiUpdateExtendedResult(resultCode="); 425 buffer.append(getResultCode()); 426 427 final int messageID = getMessageID(); 428 if (messageID >= 0) 429 { 430 buffer.append(", messageID="); 431 buffer.append(messageID); 432 } 433 434 buffer.append(", changesApplied="); 435 buffer.append(changesApplied.name()); 436 buffer.append(", results={"); 437 438 final Iterator<ObjectPair<OperationType,LDAPResult>> resultIterator = 439 results.iterator(); 440 while (resultIterator.hasNext()) 441 { 442 resultIterator.next().getSecond().toString(buffer); 443 if (resultIterator.hasNext()) 444 { 445 buffer.append(", "); 446 } 447 } 448 449 final String diagnosticMessage = getDiagnosticMessage(); 450 if (diagnosticMessage != null) 451 { 452 buffer.append(", diagnosticMessage='"); 453 buffer.append(diagnosticMessage); 454 buffer.append('\''); 455 } 456 457 final String matchedDN = getMatchedDN(); 458 if (matchedDN != null) 459 { 460 buffer.append(", matchedDN='"); 461 buffer.append(matchedDN); 462 buffer.append('\''); 463 } 464 465 final String[] referralURLs = getReferralURLs(); 466 if (referralURLs.length > 0) 467 { 468 buffer.append(", referralURLs={"); 469 for (int i=0; i < referralURLs.length; i++) 470 { 471 if (i > 0) 472 { 473 buffer.append(", "); 474 } 475 476 buffer.append('\''); 477 buffer.append(referralURLs[i]); 478 buffer.append('\''); 479 } 480 buffer.append('}'); 481 } 482 483 final Control[] responseControls = getResponseControls(); 484 if (responseControls.length > 0) 485 { 486 buffer.append(", responseControls={"); 487 for (int i=0; i < responseControls.length; i++) 488 { 489 if (i > 0) 490 { 491 buffer.append(", "); 492 } 493 494 buffer.append(responseControls[i]); 495 } 496 buffer.append('}'); 497 } 498 499 buffer.append(')'); 500 } 501 }