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 }