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