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