001/* 002 * Copyright 2008-2018 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2015-2018 Ping Identity Corporation 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 */ 021package com.unboundid.ldap.sdk.unboundidds.controls; 022 023 024 025import java.util.ArrayList; 026import java.util.Collections; 027import java.util.List; 028 029import com.unboundid.asn1.ASN1Boolean; 030import com.unboundid.asn1.ASN1Element; 031import com.unboundid.asn1.ASN1OctetString; 032import com.unboundid.asn1.ASN1Sequence; 033import com.unboundid.ldap.sdk.Control; 034import com.unboundid.ldap.sdk.DecodeableControl; 035import com.unboundid.ldap.sdk.LDAPException; 036import com.unboundid.ldap.sdk.LDAPResult; 037import com.unboundid.ldap.sdk.ResultCode; 038import com.unboundid.ldap.sdk.unboundidds.extensions. 039 StartInteractiveTransactionExtendedRequest; 040import com.unboundid.util.NotMutable; 041import com.unboundid.util.ThreadSafety; 042import com.unboundid.util.ThreadSafetyLevel; 043 044import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*; 045import static com.unboundid.util.StaticUtils.*; 046 047 048 049/** 050 * This class defines an interactive transaction specification response control, 051 * which will be included in the server's response to an operation that included 052 * the {@link InteractiveTransactionSpecificationRequestControl}. 053 * <BR> 054 * <BLOCKQUOTE> 055 * <B>NOTE:</B> This class, and other classes within the 056 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 057 * supported for use against Ping Identity, UnboundID, and Alcatel-Lucent 8661 058 * server products. These classes provide support for proprietary 059 * functionality or for external specifications that are not considered stable 060 * or mature enough to be guaranteed to work in an interoperable way with 061 * other types of LDAP servers. 062 * </BLOCKQUOTE> 063 * <BR> 064 * It provides information about the state of the transaction, which may 065 * include: 066 * <UL> 067 * <LI><CODE>transactionValid</CODE> -- Indicates whether the transaction is 068 * still valid in the server. This should be checked if the associated 069 * operation did not complete successfully.</LI> 070 * <LI><CODE>baseDNs</CODE> -- This may specify the set of base DNs below 071 * which the client is allowed to request operations as part of this 072 * transaction. It may be absent if there are no restrictions on which 073 * base DNs may be used, or if it has not changed since the last 074 * response within this transaction.</LI> 075 * </UL> 076 * See the documentation in the 077 * {@link StartInteractiveTransactionExtendedRequest} class for an example of 078 * processing interactive transactions. 079 */ 080@NotMutable() 081@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 082public final class InteractiveTransactionSpecificationResponseControl 083 extends Control 084 implements DecodeableControl 085{ 086 /** 087 * The OID (1.3.6.1.4.1.30221.2.5.4) for the interactive transaction 088 * specification response control. 089 */ 090 public static final String 091 INTERACTIVE_TRANSACTION_SPECIFICATION_RESPONSE_OID = 092 "1.3.6.1.4.1.30221.2.5.4"; 093 094 095 096 /** 097 * The BER type for the {@code transactionValid} element of the control value. 098 */ 099 private static final byte TYPE_TXN_VALID = (byte) 0x80; 100 101 102 103 /** 104 * The BER type for the {@code baseDNs} element of the control value. 105 */ 106 private static final byte TYPE_BASE_DNS = (byte) 0xA1; 107 108 109 110 /** 111 * The serial version UID for this serializable class. 112 */ 113 private static final long serialVersionUID = -4323085263241417543L; 114 115 116 117 // The flag that indicates whether the associated transaction is still valid. 118 private final boolean transactionValid; 119 120 // The set of base DNs that may be targeted by this transaction. 121 private final List<String> baseDNs; 122 123 124 125 // This is an ugly hack to prevent checkstyle from complaining about imports 126 // for classes that are needed by javadoc @link elements but aren't otherwise 127 // used in the class. It appears that checkstyle does not recognize the use 128 // of these classes in javadoc @link elements so we must ensure that they are 129 // referenced elsewhere in the class to prevent checkstyle from complaining. 130 static 131 { 132 final StartInteractiveTransactionExtendedRequest r = null; 133 } 134 135 136 137 /** 138 * Creates a new empty control instance that is intended to be used only for 139 * decoding controls via the {@code DecodeableControl} interface. 140 */ 141 InteractiveTransactionSpecificationResponseControl() 142 { 143 transactionValid = false; 144 baseDNs = null; 145 } 146 147 148 149 /** 150 * Creates a new interactive transaction specification response control with 151 * the provided information. It will not be marked critical. 152 * 153 * @param transactionValid Indicates whether the associated transaction is 154 * still valid. 155 * @param baseDNs The set of base DNs that may be targeted over the 156 * course of the transaction. It may be 157 * {@code null} if there are no restrictions or the 158 * set of restrictions has not changed since the 159 * last response. 160 */ 161 public InteractiveTransactionSpecificationResponseControl( 162 final boolean transactionValid, final List<String> baseDNs) 163 { 164 super(INTERACTIVE_TRANSACTION_SPECIFICATION_RESPONSE_OID, false, 165 encodeValue(transactionValid, baseDNs)); 166 167 this.transactionValid = transactionValid; 168 169 if (baseDNs == null) 170 { 171 this.baseDNs = null; 172 } 173 else 174 { 175 this.baseDNs = 176 Collections.unmodifiableList(new ArrayList<String>(baseDNs)); 177 } 178 } 179 180 181 182 /** 183 * Creates a new interactive transaction specification response control with 184 * the provided information. 185 * 186 * @param oid The OID for the control. 187 * @param isCritical Indicates whether the control should be marked 188 * critical. 189 * @param value The encoded value for the control. This may be 190 * {@code null} if no value was provided. 191 * 192 * @throws LDAPException If the provided control cannot be decoded as an 193 * interactive transaction specification response 194 * control. 195 */ 196 public InteractiveTransactionSpecificationResponseControl(final String oid, 197 final boolean isCritical, final ASN1OctetString value) 198 throws LDAPException 199 { 200 super(oid, isCritical, value); 201 202 if (value == null) 203 { 204 throw new LDAPException(ResultCode.DECODING_ERROR, 205 ERR_INT_TXN_RESPONSE_NO_VALUE.get()); 206 } 207 208 final ASN1Element[] elements; 209 try 210 { 211 final ASN1Element valueElement = ASN1Element.decode(value.getValue()); 212 elements = ASN1Sequence.decodeAsSequence(valueElement).elements(); 213 } 214 catch (final Exception e) 215 { 216 throw new LDAPException(ResultCode.DECODING_ERROR, 217 ERR_INT_TXN_RESPONSE_VALUE_NOT_SEQUENCE.get( 218 e.getMessage()), e); 219 } 220 221 Boolean isValid = null; 222 List<String> baseDNList = null; 223 224 for (final ASN1Element element : elements) 225 { 226 switch (element.getType()) 227 { 228 case TYPE_TXN_VALID: 229 try 230 { 231 isValid = ASN1Boolean.decodeAsBoolean(element).booleanValue(); 232 } 233 catch (final Exception e) 234 { 235 throw new LDAPException(ResultCode.DECODING_ERROR, 236 ERR_INT_TXN_RESPONSE_TXN_VALID_NOT_BOOLEAN.get(e.getMessage()), 237 e); 238 } 239 break; 240 case TYPE_BASE_DNS: 241 try 242 { 243 final ASN1Sequence s = ASN1Sequence.decodeAsSequence(element); 244 baseDNList = new ArrayList<String>(s.elements().length); 245 for (final ASN1Element e : s.elements()) 246 { 247 baseDNList.add( 248 ASN1OctetString.decodeAsOctetString(e).stringValue()); 249 } 250 } 251 catch (final Exception e) 252 { 253 throw new LDAPException(ResultCode.DECODING_ERROR, 254 ERR_INT_TXN_RESPONSE_BASE_DNS_NOT_SEQUENCE.get(e.getMessage()), 255 e); 256 } 257 break; 258 default: 259 throw new LDAPException(ResultCode.DECODING_ERROR, 260 ERR_INT_TXN_RESPONSE_INVALID_ELEMENT_TYPE.get( 261 toHex(element.getType()))); 262 } 263 } 264 265 if (isValid == null) 266 { 267 throw new LDAPException(ResultCode.DECODING_ERROR, 268 ERR_INT_TXN_RESPONSE_NO_TXN_VALID.get()); 269 } 270 271 transactionValid = isValid; 272 273 if (baseDNList == null) 274 { 275 baseDNs = null; 276 } 277 else 278 { 279 baseDNs = Collections.unmodifiableList(baseDNList); 280 } 281 } 282 283 284 285 /** 286 * Encodes the provided information into an ASN.1 octet string suitable for 287 * use as the value of this control. 288 * 289 * @param transactionValid Indicates whether the associated transaction is 290 * still valid. 291 * @param baseDNs The set of base DNs that may be targeted over the 292 * course of the transaction. It may be 293 * {@code null} if there are no restrictions or the 294 * set of restrictions has not changed since the 295 * last response. 296 * 297 * @return The ASN1 octet string that may be used as the control value. 298 */ 299 private static ASN1OctetString encodeValue(final boolean transactionValid, 300 final List<String> baseDNs) 301 { 302 final ASN1Element[] elements; 303 if (baseDNs == null) 304 { 305 elements = new ASN1Element[] 306 { 307 new ASN1Boolean(TYPE_TXN_VALID, transactionValid) 308 }; 309 } 310 else 311 { 312 final ASN1Element[] baseDNElements = new ASN1Element[baseDNs.size()]; 313 for (int i=0; i < baseDNElements.length; i++) 314 { 315 baseDNElements[i] = new ASN1OctetString(baseDNs.get(i)); 316 } 317 318 elements = new ASN1Element[] 319 { 320 new ASN1Boolean(TYPE_TXN_VALID, transactionValid), 321 new ASN1Sequence(TYPE_BASE_DNS, baseDNElements) 322 }; 323 } 324 325 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 326 } 327 328 329 330 /** 331 * {@inheritDoc} 332 */ 333 @Override() 334 public InteractiveTransactionSpecificationResponseControl decodeControl( 335 final String oid, final boolean isCritical, 336 final ASN1OctetString value) 337 throws LDAPException 338 { 339 return new InteractiveTransactionSpecificationResponseControl(oid, 340 isCritical, value); 341 } 342 343 344 345 /** 346 * Extracts an interactive transaction specification response control from the 347 * provided result. 348 * 349 * @param result The result from which to retrieve the interactive 350 * transaction specification response control. 351 * 352 * @return The interactive transaction specification response control 353 * contained in the provided result, or {@code null} if the result 354 * did not contain an interactive transaction specification response 355 * control. 356 * 357 * @throws LDAPException If a problem is encountered while attempting to 358 * decode the interactive transaction specification 359 * response control contained in the provided result. 360 */ 361 public static InteractiveTransactionSpecificationResponseControl 362 get(final LDAPResult result) 363 throws LDAPException 364 { 365 final Control c = result.getResponseControl( 366 INTERACTIVE_TRANSACTION_SPECIFICATION_RESPONSE_OID); 367 if (c == null) 368 { 369 return null; 370 } 371 372 if (c instanceof InteractiveTransactionSpecificationResponseControl) 373 { 374 return (InteractiveTransactionSpecificationResponseControl) c; 375 } 376 else 377 { 378 return new InteractiveTransactionSpecificationResponseControl(c.getOID(), 379 c.isCritical(), c.getValue()); 380 } 381 } 382 383 384 385 /** 386 * Indicates whether the associated transaction is still valid on the server. 387 * 388 * @return {@code true} if the associated transaction is still valid on the 389 * server and may be used for future operations, or {@code false} if 390 * the transaction has been aborted and may no longer be used. 391 */ 392 public boolean transactionValid() 393 { 394 return transactionValid; 395 } 396 397 398 399 /** 400 * Retrieves the set of base DNs below which operations which are part of the 401 * transaction may be performed. 402 * 403 * @return The set of base DNs below which operations may be performed as 404 * part of the transaction, or {@code null} if there are no 405 * restrictions or if the set of restrictions has not changed since 406 * the last response. 407 */ 408 public List<String> getBaseDNs() 409 { 410 return baseDNs; 411 } 412 413 414 415 /** 416 * {@inheritDoc} 417 */ 418 @Override() 419 public String getControlName() 420 { 421 return INFO_CONTROL_NAME_INTERACTIVE_TXN_RESPONSE.get(); 422 } 423 424 425 426 /** 427 * {@inheritDoc} 428 */ 429 @Override() 430 public void toString(final StringBuilder buffer) 431 { 432 buffer.append("InteractiveTransactionSpecificationResponseControl("); 433 buffer.append("transactionValid="); 434 buffer.append(transactionValid); 435 buffer.append(", baseDNs="); 436 if (baseDNs == null) 437 { 438 buffer.append("null"); 439 } 440 else 441 { 442 buffer.append('{'); 443 for (int i=0; i < baseDNs.size(); i++) 444 { 445 if (i > 0) 446 { 447 buffer.append(", "); 448 } 449 450 buffer.append('\''); 451 buffer.append(baseDNs.get(i)); 452 buffer.append('\''); 453 } 454 buffer.append('}'); 455 } 456 457 buffer.append(", isCritical="); 458 buffer.append(isCritical()); 459 buffer.append(')'); 460 } 461}