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.ThreadSafety; 040import com.unboundid.util.ThreadSafetyLevel; 041 042import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*; 043import static com.unboundid.util.StaticUtils.*; 044import static com.unboundid.util.Validator.*; 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 Alcatel-Lucent 8661 062 * server products. These classes provide support for proprietary 063 * functionality or for external specifications that are not considered stable 064 * or mature enough to be guaranteed to work in an interoperable way with 065 * 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 // This is an ugly hack to prevent checkstyle from complaining about imports 143 // for classes that are needed by javadoc @link elements but aren't otherwise 144 // used in the class. It appears that checkstyle does not recognize the use 145 // of these classes in javadoc @link elements so we must ensure that they are 146 // referenced elsewhere in the class to prevent checkstyle from complaining. 147 static 148 { 149 final StartInteractiveTransactionExtendedRequest r1 = null; 150 final StartInteractiveTransactionExtendedResult r2 = null; 151 } 152 153 154 155 /** 156 * Creates a new interactive transaction specification request control with 157 * the provided transaction ID. The server will attempt to keep the 158 * transaction active in the event of a failure and will obtain write locks on 159 * targeted entries. 160 * 161 * @param transactionID The transaction ID for the associated transaction, 162 * as obtained from the start interactive transaction 163 * extended operation. It must not be {@code null}. 164 */ 165 public InteractiveTransactionSpecificationRequestControl( 166 final ASN1OctetString transactionID) 167 { 168 this(transactionID, false, true); 169 } 170 171 172 173 /** 174 * Creates a new interactive transaction specification request control with 175 * the provided information. 176 * 177 * @param transactionID The transaction ID for the associated transaction, 178 * as obtained from the start interactive transaction 179 * extended operation. It must not be {@code null}. 180 * @param abortOnFailure Indicates whether the transaction should be aborted 181 * if the associated operation does not complete 182 * successfully. 183 * @param writeLock Indicates whether the server should attempt to 184 * obtain a write lock on the target entry. This 185 * should only be {@code false} if the associated 186 * operation is a search or compare and it is known 187 * that the target entry will not be updated later in 188 * the transaction. 189 */ 190 public InteractiveTransactionSpecificationRequestControl( 191 final ASN1OctetString transactionID, final boolean abortOnFailure, 192 final boolean writeLock) 193 { 194 super(INTERACTIVE_TRANSACTION_SPECIFICATION_REQUEST_OID, true, 195 encodeValue(transactionID, abortOnFailure, writeLock)); 196 197 this.transactionID = transactionID; 198 this.abortOnFailure = abortOnFailure; 199 this.writeLock = writeLock; 200 } 201 202 203 204 /** 205 * Creates a new interactive transaction specification request control which 206 * is decoded from the provided generic control. 207 * 208 * @param control The generic control to be decoded as an interactive 209 * transaction specification request control. 210 * 211 * @throws LDAPException If the provided control cannot be decoded as an 212 * interactive transaction specification request 213 * control. 214 */ 215 public InteractiveTransactionSpecificationRequestControl( 216 final Control control) 217 throws LDAPException 218 { 219 super(control); 220 221 if (! control.hasValue()) 222 { 223 throw new LDAPException(ResultCode.DECODING_ERROR, 224 ERR_INT_TXN_REQUEST_NO_VALUE.get()); 225 } 226 227 final ASN1Element[] elements; 228 try 229 { 230 final ASN1Element e = ASN1Element.decode(control.getValue().getValue()); 231 elements = ASN1Sequence.decodeAsSequence(e).elements(); 232 } 233 catch (final Exception e) 234 { 235 throw new LDAPException(ResultCode.DECODING_ERROR, 236 ERR_INT_TXN_REQUEST_VALUE_NOT_SEQUENCE.get(e.getMessage()), e); 237 } 238 239 ASN1OctetString txnID = null; 240 boolean shouldAbortOnFailure = false; 241 boolean shouldWriteLock = true; 242 243 for (final ASN1Element element : elements) 244 { 245 switch (element.getType()) 246 { 247 case TYPE_TXN_ID: 248 txnID = ASN1OctetString.decodeAsOctetString(element); 249 break; 250 case TYPE_ABORT_ON_FAILURE: 251 try 252 { 253 shouldAbortOnFailure = 254 ASN1Boolean.decodeAsBoolean(element).booleanValue(); 255 } 256 catch (final Exception e) 257 { 258 throw new LDAPException(ResultCode.DECODING_ERROR, 259 ERR_INT_TXN_REQUEST_ABORT_ON_FAILURE_NOT_BOOLEAN.get( 260 e.getMessage()), e); 261 } 262 break; 263 case TYPE_WRITE_LOCK: 264 try 265 { 266 shouldWriteLock = 267 ASN1Boolean.decodeAsBoolean(element).booleanValue(); 268 } 269 catch (final Exception e) 270 { 271 throw new LDAPException(ResultCode.DECODING_ERROR, 272 ERR_INT_TXN_REQUEST_WRITE_LOCK_NOT_BOOLEAN.get(e.getMessage()), 273 e); 274 } 275 break; 276 default: 277 throw new LDAPException(ResultCode.DECODING_ERROR, 278 ERR_INT_TXN_REQUEST_INVALID_ELEMENT_TYPE.get( 279 toHex(element.getType()))); 280 } 281 } 282 283 if (txnID == null) 284 { 285 throw new LDAPException(ResultCode.DECODING_ERROR, 286 ERR_INT_TXN_REQUEST_NO_TXN_ID.get()); 287 } 288 289 transactionID = txnID; 290 abortOnFailure = shouldAbortOnFailure; 291 writeLock = shouldWriteLock; 292 } 293 294 295 296 /** 297 * Encodes the provided information into an ASN.1 octet string suitable for 298 * use as the value of this control. 299 * 300 * @param transactionID The transaction ID for the associated transaction, 301 * as obtained from the start interactive transaction 302 * extended operation. It must not be {@code null}. 303 * @param abortOnFailure Indicates whether the transaction should be aborted 304 * if the associated operation does not complete 305 * successfully. 306 * @param writeLock Indicates whether the server should attempt to 307 * obtain a write lock on the target entry. This 308 * should only be {@code false} if the associated 309 * operation is a search or compare and it is known 310 * that the target entry will not be updated later in 311 * the transaction. 312 * 313 * @return The ASN.1 octet string containing the encoded value for this 314 * control. 315 */ 316 private static ASN1OctetString encodeValue( 317 final ASN1OctetString transactionID, 318 final boolean abortOnFailure, final boolean writeLock) 319 { 320 ensureNotNull(transactionID); 321 322 final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(3); 323 elements.add(new ASN1OctetString(TYPE_TXN_ID, transactionID.getValue())); 324 325 if (abortOnFailure) 326 { 327 elements.add(new ASN1Boolean(TYPE_ABORT_ON_FAILURE, abortOnFailure)); 328 } 329 330 if (! writeLock) 331 { 332 elements.add(new ASN1Boolean(TYPE_WRITE_LOCK, writeLock)); 333 } 334 335 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 336 } 337 338 339 340 /** 341 * Retrieves the transaction ID for the associated transaction. 342 * 343 * @return The transaction ID for the associated transaction. 344 */ 345 public ASN1OctetString getTransactionID() 346 { 347 return transactionID; 348 } 349 350 351 352 /** 353 * Indicates whether the transaction should be aborted if the associated 354 * operation does not complete successfully. 355 * 356 * @return {@code true} if the transaction should be aborted if the 357 * associated operation does not complete successfully, or 358 * {@code false} if the server should attempt to keep the transaction 359 * active if the associated operation does not complete successfully. 360 */ 361 public boolean abortOnFailure() 362 { 363 return abortOnFailure; 364 } 365 366 367 368 /** 369 * Indicates whether the server should attempt to obtain a write lock on 370 * entries targeted by the associated operation. 371 * 372 * @return {@code true} if the server should attempt to obtain a write lock 373 * on entries targeted by the associated operation, or {@code false} 374 * if a read lock is acceptable as the entries are not expected to 375 * be altered later in the transaction. 376 */ 377 public boolean writeLock() 378 { 379 return writeLock; 380 } 381 382 383 384 /** 385 * {@inheritDoc} 386 */ 387 @Override() 388 public String getControlName() 389 { 390 return INFO_CONTROL_NAME_INTERACTIVE_TXN_REQUEST.get(); 391 } 392 393 394 395 /** 396 * {@inheritDoc} 397 */ 398 @Override() 399 public void toString(final StringBuilder buffer) 400 { 401 buffer.append("InteractiveTransactionSpecificationRequestControl(" + 402 "transactionID='"); 403 buffer.append(transactionID.stringValue()); 404 buffer.append("', abortOnFailure="); 405 buffer.append(abortOnFailure); 406 buffer.append(", writeLock="); 407 buffer.append(writeLock); 408 buffer.append(')'); 409 } 410}