001/* 002 * Copyright 2008-2019 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2015-2019 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.StaticUtils; 042import com.unboundid.util.ThreadSafety; 043import com.unboundid.util.ThreadSafetyLevel; 044 045import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*; 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 058 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 059 * for proprietary functionality or for external specifications that are not 060 * considered stable or mature enough to be guaranteed to work in an 061 * interoperable way with 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 /** 126 * Creates a new empty control instance that is intended to be used only for 127 * decoding controls via the {@code DecodeableControl} interface. 128 */ 129 InteractiveTransactionSpecificationResponseControl() 130 { 131 transactionValid = false; 132 baseDNs = null; 133 } 134 135 136 137 /** 138 * Creates a new interactive transaction specification response control with 139 * the provided information. It will not be marked critical. 140 * 141 * @param transactionValid Indicates whether the associated transaction is 142 * still valid. 143 * @param baseDNs The set of base DNs that may be targeted over the 144 * course of the transaction. It may be 145 * {@code null} if there are no restrictions or the 146 * set of restrictions has not changed since the 147 * last response. 148 */ 149 public InteractiveTransactionSpecificationResponseControl( 150 final boolean transactionValid, final List<String> baseDNs) 151 { 152 super(INTERACTIVE_TRANSACTION_SPECIFICATION_RESPONSE_OID, false, 153 encodeValue(transactionValid, baseDNs)); 154 155 this.transactionValid = transactionValid; 156 157 if (baseDNs == null) 158 { 159 this.baseDNs = null; 160 } 161 else 162 { 163 this.baseDNs = 164 Collections.unmodifiableList(new ArrayList<>(baseDNs)); 165 } 166 } 167 168 169 170 /** 171 * Creates a new interactive transaction specification response control with 172 * the provided information. 173 * 174 * @param oid The OID for the control. 175 * @param isCritical Indicates whether the control should be marked 176 * critical. 177 * @param value The encoded value for the control. This may be 178 * {@code null} if no value was provided. 179 * 180 * @throws LDAPException If the provided control cannot be decoded as an 181 * interactive transaction specification response 182 * control. 183 */ 184 public InteractiveTransactionSpecificationResponseControl(final String oid, 185 final boolean isCritical, final ASN1OctetString value) 186 throws LDAPException 187 { 188 super(oid, isCritical, value); 189 190 if (value == null) 191 { 192 throw new LDAPException(ResultCode.DECODING_ERROR, 193 ERR_INT_TXN_RESPONSE_NO_VALUE.get()); 194 } 195 196 final ASN1Element[] elements; 197 try 198 { 199 final ASN1Element valueElement = ASN1Element.decode(value.getValue()); 200 elements = ASN1Sequence.decodeAsSequence(valueElement).elements(); 201 } 202 catch (final Exception e) 203 { 204 throw new LDAPException(ResultCode.DECODING_ERROR, 205 ERR_INT_TXN_RESPONSE_VALUE_NOT_SEQUENCE.get( 206 e.getMessage()), e); 207 } 208 209 Boolean isValid = null; 210 List<String> baseDNList = null; 211 212 for (final ASN1Element element : elements) 213 { 214 switch (element.getType()) 215 { 216 case TYPE_TXN_VALID: 217 try 218 { 219 isValid = ASN1Boolean.decodeAsBoolean(element).booleanValue(); 220 } 221 catch (final Exception e) 222 { 223 throw new LDAPException(ResultCode.DECODING_ERROR, 224 ERR_INT_TXN_RESPONSE_TXN_VALID_NOT_BOOLEAN.get(e.getMessage()), 225 e); 226 } 227 break; 228 case TYPE_BASE_DNS: 229 try 230 { 231 final ASN1Sequence s = ASN1Sequence.decodeAsSequence(element); 232 baseDNList = new ArrayList<>(s.elements().length); 233 for (final ASN1Element e : s.elements()) 234 { 235 baseDNList.add( 236 ASN1OctetString.decodeAsOctetString(e).stringValue()); 237 } 238 } 239 catch (final Exception e) 240 { 241 throw new LDAPException(ResultCode.DECODING_ERROR, 242 ERR_INT_TXN_RESPONSE_BASE_DNS_NOT_SEQUENCE.get(e.getMessage()), 243 e); 244 } 245 break; 246 default: 247 throw new LDAPException(ResultCode.DECODING_ERROR, 248 ERR_INT_TXN_RESPONSE_INVALID_ELEMENT_TYPE.get( 249 StaticUtils.toHex(element.getType()))); 250 } 251 } 252 253 if (isValid == null) 254 { 255 throw new LDAPException(ResultCode.DECODING_ERROR, 256 ERR_INT_TXN_RESPONSE_NO_TXN_VALID.get()); 257 } 258 259 transactionValid = isValid; 260 261 if (baseDNList == null) 262 { 263 baseDNs = null; 264 } 265 else 266 { 267 baseDNs = Collections.unmodifiableList(baseDNList); 268 } 269 } 270 271 272 273 /** 274 * Encodes the provided information into an ASN.1 octet string suitable for 275 * use as the value of this control. 276 * 277 * @param transactionValid Indicates whether the associated transaction is 278 * still valid. 279 * @param baseDNs The set of base DNs that may be targeted over the 280 * course of the transaction. It may be 281 * {@code null} if there are no restrictions or the 282 * set of restrictions has not changed since the 283 * last response. 284 * 285 * @return The ASN1 octet string that may be used as the control value. 286 */ 287 private static ASN1OctetString encodeValue(final boolean transactionValid, 288 final List<String> baseDNs) 289 { 290 final ASN1Element[] elements; 291 if (baseDNs == null) 292 { 293 elements = new ASN1Element[] 294 { 295 new ASN1Boolean(TYPE_TXN_VALID, transactionValid) 296 }; 297 } 298 else 299 { 300 final ASN1Element[] baseDNElements = new ASN1Element[baseDNs.size()]; 301 for (int i=0; i < baseDNElements.length; i++) 302 { 303 baseDNElements[i] = new ASN1OctetString(baseDNs.get(i)); 304 } 305 306 elements = new ASN1Element[] 307 { 308 new ASN1Boolean(TYPE_TXN_VALID, transactionValid), 309 new ASN1Sequence(TYPE_BASE_DNS, baseDNElements) 310 }; 311 } 312 313 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 314 } 315 316 317 318 /** 319 * {@inheritDoc} 320 */ 321 @Override() 322 public InteractiveTransactionSpecificationResponseControl decodeControl( 323 final String oid, final boolean isCritical, 324 final ASN1OctetString value) 325 throws LDAPException 326 { 327 return new InteractiveTransactionSpecificationResponseControl(oid, 328 isCritical, value); 329 } 330 331 332 333 /** 334 * Extracts an interactive transaction specification response control from the 335 * provided result. 336 * 337 * @param result The result from which to retrieve the interactive 338 * transaction specification response control. 339 * 340 * @return The interactive transaction specification response control 341 * contained in the provided result, or {@code null} if the result 342 * did not contain an interactive transaction specification response 343 * control. 344 * 345 * @throws LDAPException If a problem is encountered while attempting to 346 * decode the interactive transaction specification 347 * response control contained in the provided result. 348 */ 349 public static InteractiveTransactionSpecificationResponseControl 350 get(final LDAPResult result) 351 throws LDAPException 352 { 353 final Control c = result.getResponseControl( 354 INTERACTIVE_TRANSACTION_SPECIFICATION_RESPONSE_OID); 355 if (c == null) 356 { 357 return null; 358 } 359 360 if (c instanceof InteractiveTransactionSpecificationResponseControl) 361 { 362 return (InteractiveTransactionSpecificationResponseControl) c; 363 } 364 else 365 { 366 return new InteractiveTransactionSpecificationResponseControl(c.getOID(), 367 c.isCritical(), c.getValue()); 368 } 369 } 370 371 372 373 /** 374 * Indicates whether the associated transaction is still valid on the server. 375 * 376 * @return {@code true} if the associated transaction is still valid on the 377 * server and may be used for future operations, or {@code false} if 378 * the transaction has been aborted and may no longer be used. 379 */ 380 public boolean transactionValid() 381 { 382 return transactionValid; 383 } 384 385 386 387 /** 388 * Retrieves the set of base DNs below which operations which are part of the 389 * transaction may be performed. 390 * 391 * @return The set of base DNs below which operations may be performed as 392 * part of the transaction, or {@code null} if there are no 393 * restrictions or if the set of restrictions has not changed since 394 * the last response. 395 */ 396 public List<String> getBaseDNs() 397 { 398 return baseDNs; 399 } 400 401 402 403 /** 404 * {@inheritDoc} 405 */ 406 @Override() 407 public String getControlName() 408 { 409 return INFO_CONTROL_NAME_INTERACTIVE_TXN_RESPONSE.get(); 410 } 411 412 413 414 /** 415 * {@inheritDoc} 416 */ 417 @Override() 418 public void toString(final StringBuilder buffer) 419 { 420 buffer.append("InteractiveTransactionSpecificationResponseControl("); 421 buffer.append("transactionValid="); 422 buffer.append(transactionValid); 423 buffer.append(", baseDNs="); 424 if (baseDNs == null) 425 { 426 buffer.append("null"); 427 } 428 else 429 { 430 buffer.append('{'); 431 for (int i=0; i < baseDNs.size(); i++) 432 { 433 if (i > 0) 434 { 435 buffer.append(", "); 436 } 437 438 buffer.append('\''); 439 buffer.append(baseDNs.get(i)); 440 buffer.append('\''); 441 } 442 buffer.append('}'); 443 } 444 445 buffer.append(", isCritical="); 446 buffer.append(isCritical()); 447 buffer.append(')'); 448 } 449}