001 /*
002 * Copyright 2008-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.text.ParseException;
026 import java.util.ArrayList;
027 import java.util.Collections;
028 import java.util.Date;
029 import java.util.Iterator;
030 import java.util.LinkedHashMap;
031 import java.util.Map;
032 import java.util.NoSuchElementException;
033
034 import com.unboundid.asn1.ASN1Element;
035 import com.unboundid.asn1.ASN1OctetString;
036 import com.unboundid.asn1.ASN1Sequence;
037 import com.unboundid.ldap.sdk.Control;
038 import com.unboundid.ldap.sdk.ExtendedResult;
039 import com.unboundid.ldap.sdk.LDAPException;
040 import com.unboundid.ldap.sdk.ResultCode;
041 import com.unboundid.util.NotMutable;
042 import com.unboundid.util.ThreadSafety;
043 import com.unboundid.util.ThreadSafetyLevel;
044
045 import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*;
046 import static com.unboundid.util.Debug.*;
047
048
049
050 /**
051 * <BLOCKQUOTE>
052 * <B>NOTE:</B> This class is part of the Commercial Edition of the UnboundID
053 * LDAP SDK for Java. It is not available for use in applications that
054 * include only the Standard Edition of the LDAP SDK, and is not supported for
055 * use in conjunction with non-UnboundID products.
056 * </BLOCKQUOTE>
057 * This class implements a data structure for storing the information from an
058 * extended result for the password policy state extended request as used in the
059 * UnboundID Directory Server. It is able to decode a generic extended result
060 * to obtain the user DN and operations. See the documentation in the
061 * {@link PasswordPolicyStateExtendedRequest} class for an example that
062 * demonstrates the use of the password policy state extended operation.
063 */
064 @NotMutable()
065 @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
066 public final class PasswordPolicyStateExtendedResult
067 extends ExtendedResult
068 {
069 /**
070 * The serial version UID for this serializable class.
071 */
072 private static final long serialVersionUID = 7140468768443263344L;
073
074
075
076 // A map containing all of the response operations, indexed by operation type.
077 private final Map<Integer,PasswordPolicyStateOperation> operations;
078
079 // The user DN from the response.
080 private final String userDN;
081
082
083
084 /**
085 * Creates a new password policy state extended result from the provided
086 * extended result.
087 *
088 * @param extendedResult The extended result to be decoded as a password
089 * policy state extended result. It must not be
090 * {@code null}.
091 *
092 * @throws LDAPException If the provided extended result cannot be decoded
093 * as a password policy state extended result.
094 */
095 public PasswordPolicyStateExtendedResult(final ExtendedResult extendedResult)
096 throws LDAPException
097 {
098 super(extendedResult);
099
100 final ASN1OctetString value = extendedResult.getValue();
101 if (value == null)
102 {
103 userDN = null;
104 operations = Collections.emptyMap();
105 return;
106 }
107
108 final ASN1Element[] elements;
109 try
110 {
111 final ASN1Element valueElement = ASN1Element.decode(value.getValue());
112 elements = ASN1Sequence.decodeAsSequence(valueElement).elements();
113 }
114 catch (Exception e)
115 {
116 debugException(e);
117 throw new LDAPException(ResultCode.DECODING_ERROR,
118 ERR_PWP_STATE_RESPONSE_VALUE_NOT_SEQUENCE.get(e),
119 e);
120 }
121
122 if ((elements.length < 1) || (elements.length > 2))
123 {
124 throw new LDAPException(ResultCode.DECODING_ERROR,
125 ERR_PWP_STATE_RESPONSE_INVALID_ELEMENT_COUNT.get(
126 elements.length));
127 }
128
129 userDN = ASN1OctetString.decodeAsOctetString(elements[0]).stringValue();
130
131 final LinkedHashMap<Integer,PasswordPolicyStateOperation> ops =
132 new LinkedHashMap<Integer,PasswordPolicyStateOperation>();
133 if (elements.length == 2)
134 {
135 try
136 {
137 final ASN1Element[] opElements =
138 ASN1Sequence.decodeAsSequence(elements[1]).elements();
139 for (final ASN1Element e : opElements)
140 {
141 final PasswordPolicyStateOperation op =
142 PasswordPolicyStateOperation.decode(e);
143 ops.put(op.getOperationType(), op);
144 }
145 }
146 catch (Exception e)
147 {
148 debugException(e);
149 throw new LDAPException(ResultCode.DECODING_ERROR,
150 ERR_PWP_STATE_RESPONSE_CANNOT_DECODE_OPS.get(e),
151 e);
152 }
153 }
154
155 operations = Collections.unmodifiableMap(ops);
156 }
157
158
159
160 /**
161 * Creates a new password policy state extended result with the provided
162 * information.
163 * @param messageID The message ID for the LDAP message that is
164 * associated with this LDAP result.
165 * @param resultCode The result code from the response.
166 * @param diagnosticMessage The diagnostic message from the response, if
167 * available.
168 * @param matchedDN The matched DN from the response, if available.
169 * @param referralURLs The set of referral URLs from the response, if
170 * available.
171 * @param userDN The user DN from the response.
172 * @param operations The set of operations from the response, mapped
173 * from operation type to the corresponding
174 * operation data.
175 * @param responseControls The set of controls from the response, if
176 * available.
177 */
178 public PasswordPolicyStateExtendedResult(final int messageID,
179 final ResultCode resultCode, final String diagnosticMessage,
180 final String matchedDN, final String[] referralURLs,
181 final String userDN,
182 final PasswordPolicyStateOperation[] operations,
183 final Control[] responseControls)
184 {
185 super(messageID, resultCode, diagnosticMessage, matchedDN, referralURLs,
186 null, encodeValue(userDN, operations), responseControls);
187
188 this.userDN = userDN;
189
190 if ((operations == null) || (operations.length == 0))
191 {
192 this.operations = Collections.emptyMap();
193 }
194 else
195 {
196 final LinkedHashMap<Integer,PasswordPolicyStateOperation> ops =
197 new LinkedHashMap<Integer,PasswordPolicyStateOperation>(
198 operations.length);
199 for (final PasswordPolicyStateOperation o : operations)
200 {
201 ops.put(o.getOperationType(), o);
202 }
203 this.operations = Collections.unmodifiableMap(ops);
204 }
205 }
206
207
208
209 /**
210 * Encodes the provided information into a suitable value for this control.
211 *
212 * @param userDN The user DN from the response.
213 * @param operations The set of operations from the response, mapped
214 * from operation type to the corresponding
215 * operation data.
216 *
217 * @return An ASN.1 octet string containing the appropriately-encoded value
218 * for this control, or {@code null} if there should not be a value.
219 */
220 private static ASN1OctetString encodeValue(final String userDN,
221 final PasswordPolicyStateOperation[] operations)
222 {
223 if ((userDN == null) && ((operations == null) || (operations.length == 0)))
224 {
225 return null;
226 }
227
228 final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(2);
229 elements.add(new ASN1OctetString(userDN));
230
231 if ((operations != null) && (operations.length > 0))
232 {
233 final ASN1Element[] opElements = new ASN1Element[operations.length];
234 for (int i=0; i < operations.length; i++)
235 {
236 opElements[i] = operations[i].encode();
237 }
238
239 elements.add(new ASN1Sequence(opElements));
240 }
241
242 return new ASN1OctetString(new ASN1Sequence(elements).encode());
243 }
244
245
246
247
248 /**
249 * Retrieves the user DN included in the response.
250 *
251 * @return The user DN included in the response, or {@code null} if the user
252 * DN is not available (e.g., if this is an error response).
253 */
254 public String getUserDN()
255 {
256 return userDN;
257 }
258
259
260
261 /**
262 * Retrieves the set of password policy operations included in the response.
263 *
264 * @return The set of password policy operations included in the response.
265 */
266 public Iterable<PasswordPolicyStateOperation> getOperations()
267 {
268 return operations.values();
269 }
270
271
272
273 /**
274 * Retrieves the specified password policy state operation from the response.
275 *
276 * @param opType The operation type for the password policy state operation
277 * to retrieve.
278 *
279 * @return The requested password policy state operation, or {@code null} if
280 * no such operation was included in the response.
281 */
282 public PasswordPolicyStateOperation getOperation(final int opType)
283 {
284 return operations.get(opType);
285 }
286
287
288
289 /**
290 * Retrieves the value for the specified password policy state operation as a
291 * string.
292 *
293 * @param opType The operation type for the password policy state operation
294 * to retrieve.
295 *
296 * @return The string value of the requested password policy state operation,
297 * or {@code null} if the specified operation was not included in the
298 * response or did not have any values.
299 */
300 public String getStringValue(final int opType)
301 {
302 final PasswordPolicyStateOperation op = operations.get(opType);
303 if (op == null)
304 {
305 return null;
306 }
307
308 return op.getStringValue();
309 }
310
311
312
313 /**
314 * Retrieves the set of string values for the specified password policy state
315 * operation.
316 *
317 * @param opType The operation type for the password policy state operation
318 * to retrieve.
319 *
320 * @return The set of string values for the requested password policy state
321 * operation, or {@code null} if the specified operation was not
322 * included in the response.
323 */
324 public String[] getStringValues(final int opType)
325 {
326 final PasswordPolicyStateOperation op = operations.get(opType);
327 if (op == null)
328 {
329 return null;
330 }
331
332 return op.getStringValues();
333 }
334
335
336
337 /**
338 * Retrieves the value of the specified password policy state operation as a
339 * boolean.
340 *
341 * @param opType The operation type for the password policy state operation
342 * to retrieve.
343 *
344 * @return The boolean value of the requested password policy state
345 * operation.
346 *
347 * @throws NoSuchElementException If the specified operation was not
348 * included in the response.
349 *
350 * @throws IllegalStateException If the specified password policy state
351 * operation does not have exactly one value,
352 * or if the value cannot be parsed as a
353 * boolean value.
354 */
355 public boolean getBooleanValue(final int opType)
356 throws NoSuchElementException, IllegalStateException
357 {
358 final PasswordPolicyStateOperation op = operations.get(opType);
359 if (op == null)
360 {
361 throw new NoSuchElementException(
362 ERR_PWP_STATE_RESPONSE_NO_SUCH_OPERATION.get());
363 }
364
365 return op.getBooleanValue();
366 }
367
368
369
370 /**
371 * Retrieves the value of the specified password policy state operation as an
372 * integer.
373 *
374 * @param opType The operation type for the password policy state operation
375 * to retrieve.
376 *
377 * @return The integer value of the requested password policy state
378 * operation.
379 *
380 * @throws NoSuchElementException If the specified operation was not
381 * included in the response.
382 *
383 * @throws IllegalStateException If the value of the specified password
384 * policy state operation cannot be parsed as
385 * an integer value.
386 */
387 public int getIntValue(final int opType)
388 throws NoSuchElementException, IllegalStateException
389 {
390 final PasswordPolicyStateOperation op = operations.get(opType);
391 if (op == null)
392 {
393 throw new NoSuchElementException(
394 ERR_PWP_STATE_RESPONSE_NO_SUCH_OPERATION.get());
395 }
396
397 return op.getIntValue();
398 }
399
400
401
402 /**
403 * Retrieves the value for the specified password policy state operation as a
404 * {@code Date} in generalized time format.
405 *
406 * @param opType The operation type for the password policy state operation
407 * to retrieve.
408 *
409 * @return The value of the requested password policy state operation as a
410 * {@code Date}, or {@code null} if the specified operation was not
411 * included in the response or did not have any values.
412 *
413 * @throws ParseException If the value cannot be parsed as a date in
414 * generalized time format.
415 */
416 public Date getGeneralizedTimeValue(final int opType)
417 throws ParseException
418 {
419 final PasswordPolicyStateOperation op = operations.get(opType);
420 if (op == null)
421 {
422 return null;
423 }
424
425 return op.getGeneralizedTimeValue();
426 }
427
428
429
430 /**
431 * Retrieves the set of values for the specified password policy state
432 * operation as {@code Date}s in generalized time format.
433 *
434 * @param opType The operation type for the password policy state operation
435 * to retrieve.
436 *
437 * @return The set of values of the requested password policy state operation
438 * as {@code Date}s.
439 *
440 * @throws ParseException If any of the values cannot be parsed as a date in
441 * generalized time format.
442 */
443 public Date[] getGeneralizedTimeValues(final int opType)
444 throws ParseException
445 {
446 final PasswordPolicyStateOperation op = operations.get(opType);
447 if (op == null)
448 {
449 return null;
450 }
451
452 return op.getGeneralizedTimeValues();
453 }
454
455
456
457 /**
458 * {@inheritDoc}
459 */
460 @Override()
461 public String getExtendedResultName()
462 {
463 return INFO_EXTENDED_RESULT_NAME_PW_POLICY_STATE.get();
464 }
465
466
467
468 /**
469 * Appends a string representation of this extended result to the provided
470 * buffer.
471 *
472 * @param buffer The buffer to which a string representation of this
473 * extended result will be appended.
474 */
475 @Override()
476 public void toString(final StringBuilder buffer)
477 {
478 buffer.append("PasswordPolicyStateExtendedResult(resultCode=");
479 buffer.append(getResultCode());
480
481 final int messageID = getMessageID();
482 if (messageID >= 0)
483 {
484 buffer.append(", messageID=");
485 buffer.append(messageID);
486 }
487
488 buffer.append(", userDN='");
489 buffer.append(userDN);
490 buffer.append("', operations={");
491
492 final Iterator<PasswordPolicyStateOperation> iterator =
493 operations.values().iterator();
494 while (iterator.hasNext())
495 {
496 iterator.next().toString(buffer);
497 if (iterator.hasNext())
498 {
499 buffer.append(", ");
500 }
501 }
502 buffer.append('}');
503
504 final String diagnosticMessage = getDiagnosticMessage();
505 if (diagnosticMessage != null)
506 {
507 buffer.append(", diagnosticMessage='");
508 buffer.append(diagnosticMessage);
509 buffer.append('\'');
510 }
511
512 final String matchedDN = getMatchedDN();
513 if (matchedDN != null)
514 {
515 buffer.append(", matchedDN='");
516 buffer.append(matchedDN);
517 buffer.append('\'');
518 }
519
520 final String[] referralURLs = getReferralURLs();
521 if (referralURLs.length > 0)
522 {
523 buffer.append(", referralURLs={");
524 for (int i=0; i < referralURLs.length; i++)
525 {
526 if (i > 0)
527 {
528 buffer.append(", ");
529 }
530
531 buffer.append('\'');
532 buffer.append(referralURLs[i]);
533 buffer.append('\'');
534 }
535 buffer.append('}');
536 }
537
538 final Control[] responseControls = getResponseControls();
539 if (responseControls.length > 0)
540 {
541 buffer.append(", responseControls={");
542 for (int i=0; i < responseControls.length; i++)
543 {
544 if (i > 0)
545 {
546 buffer.append(", ");
547 }
548
549 buffer.append(responseControls[i]);
550 }
551 buffer.append('}');
552 }
553
554 buffer.append(')');
555 }
556 }