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; 026 027import com.unboundid.asn1.ASN1Boolean; 028import com.unboundid.asn1.ASN1Element; 029import com.unboundid.asn1.ASN1OctetString; 030import com.unboundid.asn1.ASN1Sequence; 031import com.unboundid.ldap.sdk.Control; 032import com.unboundid.ldap.sdk.LDAPException; 033import com.unboundid.ldap.sdk.ResultCode; 034import com.unboundid.ldap.sdk.unboundidds.extensions. 035 StartInteractiveTransactionExtendedRequest; 036import com.unboundid.ldap.sdk.unboundidds.extensions. 037 StartInteractiveTransactionExtendedResult; 038import com.unboundid.util.NotMutable; 039import com.unboundid.util.StaticUtils; 040import com.unboundid.util.ThreadSafety; 041import com.unboundid.util.ThreadSafetyLevel; 042import com.unboundid.util.Validator; 043 044import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*; 045 046 047 048/** 049 * This class provides an implementation of the interactive transaction 050 * specification request control, which may be used to indicate that the 051 * associated operation is part of an interactive transaction. It may be used 052 * in conjunction with add, compare, delete, modify, modify DN, and search 053 * requests, as well as some types of extended requests. The transaction should 054 * be created with the start interactive transaction extended request, and the 055 * end interactive transaction extended request may be used to commit or abort 056 * the associated transaction. 057 * <BR> 058 * <BLOCKQUOTE> 059 * <B>NOTE:</B> This class, and other classes within the 060 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 061 * supported for use against Ping Identity, UnboundID, and 062 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 063 * for proprietary functionality or for external specifications that are not 064 * considered stable or mature enough to be guaranteed to work in an 065 * interoperable way with other types of LDAP servers. 066 * </BLOCKQUOTE> 067 * <BR> 068 * The elements of the interactive transaction specification request control may 069 * include: 070 * <UL> 071 * <LI><CODE>txnID</CODE> -- The transaction ID for the transaction, which was 072 * obtained from a previous 073 * {@link StartInteractiveTransactionExtendedResult}.</LI> 074 * <LI><CODE>abortOnFailure</CODE> -- Indicates whether the transaction should 075 * be aborted if the request associated with this control does not 076 * complete successfully.</LI> 077 * <LI><CODE>writeLock</CODE> -- Indicates whether the target entry may be 078 * altered by this or a subsequent operation which is part of the 079 * transaction. It should generally be {@code false} only for read 080 * operations in which it is known that the target entry will not be 081 * altered by a subsequent operation.</LI> 082 * </UL> 083 * See the documentation for the 084 * {@link StartInteractiveTransactionExtendedRequest} class for an example of 085 * processing an interactive transaction. 086 */ 087@NotMutable() 088@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 089public final class InteractiveTransactionSpecificationRequestControl 090 extends Control 091{ 092 /** 093 * The OID (1.3.6.1.4.1.30221.2.5.4) for the interactive transaction 094 * specification request control. 095 */ 096 public static final String INTERACTIVE_TRANSACTION_SPECIFICATION_REQUEST_OID = 097 "1.3.6.1.4.1.30221.2.5.4"; 098 099 100 101 /** 102 * The BER type for the {@code txnID} element of the control value. 103 */ 104 private static final byte TYPE_TXN_ID = (byte) 0x80; 105 106 107 108 /** 109 * The BER type for the {@code abortOnFailure} element of the control value. 110 */ 111 private static final byte TYPE_ABORT_ON_FAILURE = (byte) 0x81; 112 113 114 115 /** 116 * The BER type for the {@code writeLock} element of the control value. 117 */ 118 private static final byte TYPE_WRITE_LOCK = (byte) 0x82; 119 120 121 122 /** 123 * The serial version UID for this serializable class. 124 */ 125 private static final long serialVersionUID = -6473934815135786621L; 126 127 128 129 // The transaction ID for the associated transaction. 130 private final ASN1OctetString transactionID; 131 132 // Indicates whether the transaction should be aborted if the associated 133 // operation does not complete successfully. 134 private final boolean abortOnFailure; 135 136 // Indicates whether the server should attempt to obtain a write lock on the 137 // target entry if the associated operation is a read operation. 138 private final boolean writeLock; 139 140 141 142 /** 143 * Creates a new interactive transaction specification request control with 144 * the provided transaction ID. The server will attempt to keep the 145 * transaction active in the event of a failure and will obtain write locks on 146 * targeted entries. 147 * 148 * @param transactionID The transaction ID for the associated transaction, 149 * as obtained from the start interactive transaction 150 * extended operation. It must not be {@code null}. 151 */ 152 public InteractiveTransactionSpecificationRequestControl( 153 final ASN1OctetString transactionID) 154 { 155 this(transactionID, false, true); 156 } 157 158 159 160 /** 161 * Creates a new interactive transaction specification request control with 162 * the provided information. 163 * 164 * @param transactionID The transaction ID for the associated transaction, 165 * as obtained from the start interactive transaction 166 * extended operation. It must not be {@code null}. 167 * @param abortOnFailure Indicates whether the transaction should be aborted 168 * if the associated operation does not complete 169 * successfully. 170 * @param writeLock Indicates whether the server should attempt to 171 * obtain a write lock on the target entry. This 172 * should only be {@code false} if the associated 173 * operation is a search or compare and it is known 174 * that the target entry will not be updated later in 175 * the transaction. 176 */ 177 public InteractiveTransactionSpecificationRequestControl( 178 final ASN1OctetString transactionID, final boolean abortOnFailure, 179 final boolean writeLock) 180 { 181 super(INTERACTIVE_TRANSACTION_SPECIFICATION_REQUEST_OID, true, 182 encodeValue(transactionID, abortOnFailure, writeLock)); 183 184 this.transactionID = transactionID; 185 this.abortOnFailure = abortOnFailure; 186 this.writeLock = writeLock; 187 } 188 189 190 191 /** 192 * Creates a new interactive transaction specification request control which 193 * is decoded from the provided generic control. 194 * 195 * @param control The generic control to be decoded as an interactive 196 * transaction specification request control. 197 * 198 * @throws LDAPException If the provided control cannot be decoded as an 199 * interactive transaction specification request 200 * control. 201 */ 202 public InteractiveTransactionSpecificationRequestControl( 203 final Control control) 204 throws LDAPException 205 { 206 super(control); 207 208 if (! control.hasValue()) 209 { 210 throw new LDAPException(ResultCode.DECODING_ERROR, 211 ERR_INT_TXN_REQUEST_NO_VALUE.get()); 212 } 213 214 final ASN1Element[] elements; 215 try 216 { 217 final ASN1Element e = ASN1Element.decode(control.getValue().getValue()); 218 elements = ASN1Sequence.decodeAsSequence(e).elements(); 219 } 220 catch (final Exception e) 221 { 222 throw new LDAPException(ResultCode.DECODING_ERROR, 223 ERR_INT_TXN_REQUEST_VALUE_NOT_SEQUENCE.get(e.getMessage()), e); 224 } 225 226 ASN1OctetString txnID = null; 227 boolean shouldAbortOnFailure = false; 228 boolean shouldWriteLock = true; 229 230 for (final ASN1Element element : elements) 231 { 232 switch (element.getType()) 233 { 234 case TYPE_TXN_ID: 235 txnID = ASN1OctetString.decodeAsOctetString(element); 236 break; 237 case TYPE_ABORT_ON_FAILURE: 238 try 239 { 240 shouldAbortOnFailure = 241 ASN1Boolean.decodeAsBoolean(element).booleanValue(); 242 } 243 catch (final Exception e) 244 { 245 throw new LDAPException(ResultCode.DECODING_ERROR, 246 ERR_INT_TXN_REQUEST_ABORT_ON_FAILURE_NOT_BOOLEAN.get( 247 e.getMessage()), e); 248 } 249 break; 250 case TYPE_WRITE_LOCK: 251 try 252 { 253 shouldWriteLock = 254 ASN1Boolean.decodeAsBoolean(element).booleanValue(); 255 } 256 catch (final Exception e) 257 { 258 throw new LDAPException(ResultCode.DECODING_ERROR, 259 ERR_INT_TXN_REQUEST_WRITE_LOCK_NOT_BOOLEAN.get(e.getMessage()), 260 e); 261 } 262 break; 263 default: 264 throw new LDAPException(ResultCode.DECODING_ERROR, 265 ERR_INT_TXN_REQUEST_INVALID_ELEMENT_TYPE.get( 266 StaticUtils.toHex(element.getType()))); 267 } 268 } 269 270 if (txnID == null) 271 { 272 throw new LDAPException(ResultCode.DECODING_ERROR, 273 ERR_INT_TXN_REQUEST_NO_TXN_ID.get()); 274 } 275 276 transactionID = txnID; 277 abortOnFailure = shouldAbortOnFailure; 278 writeLock = shouldWriteLock; 279 } 280 281 282 283 /** 284 * Encodes the provided information into an ASN.1 octet string suitable for 285 * use as the value of this control. 286 * 287 * @param transactionID The transaction ID for the associated transaction, 288 * as obtained from the start interactive transaction 289 * extended operation. It must not be {@code null}. 290 * @param abortOnFailure Indicates whether the transaction should be aborted 291 * if the associated operation does not complete 292 * successfully. 293 * @param writeLock Indicates whether the server should attempt to 294 * obtain a write lock on the target entry. This 295 * should only be {@code false} if the associated 296 * operation is a search or compare and it is known 297 * that the target entry will not be updated later in 298 * the transaction. 299 * 300 * @return The ASN.1 octet string containing the encoded value for this 301 * control. 302 */ 303 private static ASN1OctetString encodeValue( 304 final ASN1OctetString transactionID, 305 final boolean abortOnFailure, final boolean writeLock) 306 { 307 Validator.ensureNotNull(transactionID); 308 309 final ArrayList<ASN1Element> elements = new ArrayList<>(3); 310 elements.add(new ASN1OctetString(TYPE_TXN_ID, transactionID.getValue())); 311 312 if (abortOnFailure) 313 { 314 elements.add(new ASN1Boolean(TYPE_ABORT_ON_FAILURE, abortOnFailure)); 315 } 316 317 if (! writeLock) 318 { 319 elements.add(new ASN1Boolean(TYPE_WRITE_LOCK, writeLock)); 320 } 321 322 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 323 } 324 325 326 327 /** 328 * Retrieves the transaction ID for the associated transaction. 329 * 330 * @return The transaction ID for the associated transaction. 331 */ 332 public ASN1OctetString getTransactionID() 333 { 334 return transactionID; 335 } 336 337 338 339 /** 340 * Indicates whether the transaction should be aborted if the associated 341 * operation does not complete successfully. 342 * 343 * @return {@code true} if the transaction should be aborted if the 344 * associated operation does not complete successfully, or 345 * {@code false} if the server should attempt to keep the transaction 346 * active if the associated operation does not complete successfully. 347 */ 348 public boolean abortOnFailure() 349 { 350 return abortOnFailure; 351 } 352 353 354 355 /** 356 * Indicates whether the server should attempt to obtain a write lock on 357 * entries targeted by the associated operation. 358 * 359 * @return {@code true} if the server should attempt to obtain a write lock 360 * on entries targeted by the associated operation, or {@code false} 361 * if a read lock is acceptable as the entries are not expected to 362 * be altered later in the transaction. 363 */ 364 public boolean writeLock() 365 { 366 return writeLock; 367 } 368 369 370 371 /** 372 * {@inheritDoc} 373 */ 374 @Override() 375 public String getControlName() 376 { 377 return INFO_CONTROL_NAME_INTERACTIVE_TXN_REQUEST.get(); 378 } 379 380 381 382 /** 383 * {@inheritDoc} 384 */ 385 @Override() 386 public void toString(final StringBuilder buffer) 387 { 388 buffer.append("InteractiveTransactionSpecificationRequestControl(" + 389 "transactionID='"); 390 buffer.append(transactionID.stringValue()); 391 buffer.append("', abortOnFailure="); 392 buffer.append(abortOnFailure); 393 buffer.append(", writeLock="); 394 buffer.append(writeLock); 395 buffer.append(')'); 396 } 397}