001 /* 002 * Copyright 2007-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.extensions; 022 023 024 025 import com.unboundid.ldap.sdk.Control; 026 import com.unboundid.ldap.sdk.ExtendedRequest; 027 import com.unboundid.ldap.sdk.ExtendedResult; 028 import com.unboundid.ldap.sdk.LDAPConnection; 029 import com.unboundid.ldap.sdk.LDAPException; 030 import com.unboundid.ldap.sdk.ResultCode; 031 import com.unboundid.ldap.sdk.controls.SubtreeDeleteRequestControl; 032 import com.unboundid.ldap.sdk.unboundidds.controls.AccountUsableRequestControl; 033 import com.unboundid.ldap.sdk.unboundidds.controls. 034 BatchedTransactionSpecificationRequestControl; 035 import com.unboundid.ldap.sdk.unboundidds.controls. 036 IntermediateClientRequestControl; 037 import com.unboundid.ldap.sdk.unboundidds.controls.PasswordPolicyRequestControl; 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.extensions.ExtOpMessages.*; 043 044 045 046 /** 047 * <BLOCKQUOTE> 048 * <B>NOTE:</B> This class is part of the Commercial Edition of the UnboundID 049 * LDAP SDK for Java. It is not available for use in applications that 050 * include only the Standard Edition of the LDAP SDK, and is not supported for 051 * use in conjunction with non-UnboundID products. 052 * </BLOCKQUOTE> 053 * This class provides an implementation of the start batched transaction 054 * extended request. It may be used to begin a transaction that allows multiple 055 * write operations to be processed as a single atomic unit. The 056 * {@link StartBatchedTransactionExtendedResult} that is returned will include a 057 * a transaction ID. For each operation that is performed as part of the 058 * transaction, this transaction ID should be included in the corresponding 059 * request through the {@link BatchedTransactionSpecificationRequestControl}. 060 * Finally, after all requests for the transaction have been submitted to the 061 * server, the {@link EndBatchedTransactionExtendedRequest} should be used to 062 * commit that transaction, or it may also be used to abort the transaction if 063 * it is decided that it is no longer needed. 064 * <BR><BR> 065 * Transactions processed using this mechanism are called "batched transactions" 066 * because the associated requests are collected in the server and are only 067 * processed once the {@link EndBatchedTransactionExtendedRequest} has been 068 * received to indicate that the transaction should be committed. As a result, 069 * it is only possible to include write operations (in particular, add, delete, 070 * modify, modify DN, and password modify operations) in a batched transaction. 071 * Read operations (like search, bind, and compare) cannot be included in a 072 * batched transaction. However, it is possible to use some controls within the 073 * transaction and they may prove to be sufficient in many cases. The controls 074 * that can be included in operations that are part of a batched transaction 075 * include: 076 * <UL> 077 * <LI>{@link AccountUsableRequestControl}</LI> 078 * <LI>{@link com.unboundid.ldap.sdk.controls.AssertionRequestControl}</LI> 079 * <LI>{@link IntermediateClientRequestControl}</LI> 080 * <LI>{@link com.unboundid.ldap.sdk.controls.ManageDsaITRequestControl}</LI> 081 * <LI>{@link PasswordPolicyRequestControl}</LI> 082 * <LI>{@link com.unboundid.ldap.sdk.controls.PostReadRequestControl}</LI> 083 * <LI>{@link com.unboundid.ldap.sdk.controls.PreReadRequestControl}</LI> 084 * <LI>{@link SubtreeDeleteRequestControl}</LI> 085 * </UL> 086 * In particular, the assertion control may be used to ensure that an operation 087 * is only performed if the target entry matches a given filter (which allows 088 * for an atomic compare-and-swap operation), and the pre-read and post-read 089 * controls may be used to retrieve a copy of an entry immediately before or 090 * immediately after the operation was performed. 091 * <BR><BR> 092 * Note that even though the operations which are part of this transaction 093 * aren't actually processed until the end batched transaction request is 094 * received, the directory server will send back a response for each operation 095 * that is to be performed as part of the transaction. If the result of this 096 * response is {@link ResultCode#SUCCESS}, then it means that the server has 097 * accepted the operation and it will be processed when the end batched 098 * transaction request is received indicating that the transaction should be 099 * committed. However, if it has some other result then it indicates that the 100 * request may have been malformed or did not meet the requirements for the 101 * transaction (e.g., it included a control that is not allowed for a 102 * transaction). Note that even if the server returns a non-success response 103 * for an operation prior to the end batched transaction request, the 104 * transaction will still be active in the server and other operations may still 105 * be included in the transaction if desired. If it is no longer desirable to 106 * process the transaction, then the end batched transaction request should be 107 * used to abort the transaction. 108 * <BR><BR> 109 * This implementation of batched transactions is based on the 110 * specification defined in draft-zeilenga-ldap-txn, and in particular revision 111 * 11 of that draft. However, in order to ensure API stability in both the 112 * UnboundID Directory Server and the LDAP SDK for Java, this implementation 113 * will not be updated to reflect changes in the draft. In the event that the 114 * draft reaches sufficient maturity level to be published as an RFC, then a 115 * separate implementation will be provided that matches that specification. 116 * <BR><BR> 117 * <H2>Example</H2> 118 * The following example demonstrates the process for using batched 119 * transactions. It will modify two different entries as a single atomic 120 * unit. 121 * <PRE> 122 * // Use the start transaction extended operation to begin a transaction. 123 * StartBatchedTransactionExtendedResult startTxnResult; 124 * try 125 * { 126 * startTxnResult = (StartBatchedTransactionExtendedResult) 127 * connection.processExtendedOperation( 128 * new StartBatchedTransactionExtendedRequest()); 129 * // This doesn't necessarily mean that the operation was successful, since 130 * // some kinds of extended operations return non-success results under 131 * // normal conditions. 132 * } 133 * catch (LDAPException le) 134 * { 135 * // For an extended operation, this generally means that a problem was 136 * // encountered while trying to send the request or read the result. 137 * startTxnResult = new StartBatchedTransactionExtendedResult( 138 * new ExtendedResult(le)); 139 * } 140 * LDAPTestUtils.assertResultCodeEquals(startTxnResult, ResultCode.SUCCESS); 141 * ASN1OctetString txnID = startTxnResult.getTransactionID(); 142 * 143 * 144 * // At this point, we have a transaction available for use. If any problem 145 * // arises, we want to ensure that the transaction is aborted, so create a 146 * // try block to process the operations and a finally block to commit or 147 * // abort the transaction. 148 * boolean commit = false; 149 * try 150 * { 151 * // Create and process a modify operation to update a first entry as part 152 * // of the transaction. Make sure to include the transaction specification 153 * // control in the request to indicate that it should be part of the 154 * // transaction. 155 * ModifyRequest firstModifyRequest = new ModifyRequest( 156 * "cn=first,dc=example,dc=com", 157 * new Modification(ModificationType.REPLACE, "description", "first")); 158 * firstModifyRequest.addControl( 159 * new BatchedTransactionSpecificationRequestControl(txnID)); 160 * LDAPResult firstModifyResult; 161 * try 162 * { 163 * firstModifyResult = connection.modify(firstModifyRequest); 164 * } 165 * catch (LDAPException le) 166 * { 167 * firstModifyResult = le.toLDAPResult(); 168 * } 169 * LDAPTestUtils.assertResultCodeEquals(firstModifyResult, 170 * ResultCode.SUCCESS); 171 * 172 * // Perform a second modify operation as part of the transaction. 173 * ModifyRequest secondModifyRequest = new ModifyRequest( 174 * "cn=second,dc=example,dc=com", 175 * new Modification(ModificationType.REPLACE, "description", "second")); 176 * secondModifyRequest.addControl( 177 * new BatchedTransactionSpecificationRequestControl(txnID)); 178 * LDAPResult secondModifyResult; 179 * try 180 * { 181 * secondModifyResult = connection.modify(secondModifyRequest); 182 * } 183 * catch (LDAPException le) 184 * { 185 * secondModifyResult = le.toLDAPResult(); 186 * } 187 * LDAPTestUtils.assertResultCodeEquals(secondModifyResult, 188 * ResultCode.SUCCESS); 189 * 190 * // If we've gotten here, then all writes have been processed successfully 191 * // and we can indicate that the transaction should be committed rather 192 * // than aborted. 193 * commit = true; 194 * } 195 * finally 196 * { 197 * // Commit or abort the transaction. 198 * EndBatchedTransactionExtendedResult endTxnResult; 199 * try 200 * { 201 * endTxnResult = (EndBatchedTransactionExtendedResult) 202 * connection.processExtendedOperation( 203 * new EndBatchedTransactionExtendedRequest(txnID, commit)); 204 * } 205 * catch (LDAPException le) 206 * { 207 * endTxnResult = new EndBatchedTransactionExtendedResult( 208 * new ExtendedResult(le)); 209 * } 210 * LDAPTestUtils.assertResultCodeEquals(endTxnResult, ResultCode.SUCCESS); 211 * } 212 * </PRE> 213 */ 214 @NotMutable() 215 @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 216 public final class StartBatchedTransactionExtendedRequest 217 extends ExtendedRequest 218 { 219 /** 220 * The OID (1.3.6.1.4.1.30221.2.6.1) for the start batched transaction 221 * extended request. 222 */ 223 public static final String START_BATCHED_TRANSACTION_REQUEST_OID = 224 "1.3.6.1.4.1.30221.2.6.1"; 225 226 227 228 /** 229 * The serial version UID for this serializable class. 230 */ 231 private static final long serialVersionUID = 7141543268276702748L; 232 233 234 235 // This is an ugly hack to prevent checkstyle from complaining about imports 236 // for classes that are needed by javadoc @link elements but aren't otherwise 237 // used in the class. It appears that checkstyle does not recognize the use 238 // of these classes in javadoc @link elements so we must ensure that they are 239 // referenced elsewhere in the class to prevent checkstyle from complaining. 240 static 241 { 242 final AccountUsableRequestControl c1 = null; 243 final BatchedTransactionSpecificationRequestControl c2 = null; 244 final IntermediateClientRequestControl c3 = null; 245 final PasswordPolicyRequestControl c4 = null; 246 final SubtreeDeleteRequestControl c5 = null; 247 } 248 249 250 251 /** 252 * Creates a new start batched transaction extended request. 253 */ 254 public StartBatchedTransactionExtendedRequest() 255 { 256 super(START_BATCHED_TRANSACTION_REQUEST_OID); 257 } 258 259 260 261 /** 262 * Creates a new start batched transaction extended request. 263 * 264 * @param controls The set of controls to include in the request. 265 */ 266 public StartBatchedTransactionExtendedRequest(final Control[] controls) 267 { 268 super(START_BATCHED_TRANSACTION_REQUEST_OID, controls); 269 } 270 271 272 273 /** 274 * Creates a new start batched transaction extended request from the provided 275 * generic extended request. 276 * 277 * @param extendedRequest The generic extended request to use to create this 278 * start batched transaction extended request. 279 * 280 * @throws LDAPException If a problem occurs while decoding the request. 281 */ 282 public StartBatchedTransactionExtendedRequest( 283 final ExtendedRequest extendedRequest) 284 throws LDAPException 285 { 286 super(extendedRequest); 287 288 if (extendedRequest.hasValue()) 289 { 290 throw new LDAPException(ResultCode.DECODING_ERROR, 291 ERR_START_TXN_REQUEST_HAS_VALUE.get()); 292 } 293 } 294 295 296 297 /** 298 * {@inheritDoc} 299 */ 300 @Override() 301 public StartBatchedTransactionExtendedResult process( 302 final LDAPConnection connection, final int depth) 303 throws LDAPException 304 { 305 final ExtendedResult extendedResponse = super.process(connection, depth); 306 return new StartBatchedTransactionExtendedResult(extendedResponse); 307 } 308 309 310 311 /** 312 * {@inheritDoc} 313 */ 314 @Override() 315 public StartBatchedTransactionExtendedRequest duplicate() 316 { 317 return duplicate(getControls()); 318 } 319 320 321 322 /** 323 * {@inheritDoc} 324 */ 325 @Override() 326 public StartBatchedTransactionExtendedRequest duplicate( 327 final Control[] controls) 328 { 329 final StartBatchedTransactionExtendedRequest r = 330 new StartBatchedTransactionExtendedRequest(controls); 331 r.setResponseTimeoutMillis(getResponseTimeoutMillis(null)); 332 return r; 333 } 334 335 336 337 /** 338 * {@inheritDoc} 339 */ 340 @Override() 341 public String getExtendedRequestName() 342 { 343 return INFO_EXTENDED_REQUEST_NAME_START_BATCHED_TXN.get(); 344 } 345 346 347 348 /** 349 * {@inheritDoc} 350 */ 351 @Override() 352 public void toString(final StringBuilder buffer) 353 { 354 buffer.append("StartBatchedTransactionExtendedRequest("); 355 356 final Control[] controls = getControls(); 357 if (controls.length > 0) 358 { 359 buffer.append("controls={"); 360 for (int i=0; i < controls.length; i++) 361 { 362 if (i > 0) 363 { 364 buffer.append(", "); 365 } 366 367 buffer.append(controls[i]); 368 } 369 buffer.append('}'); 370 } 371 372 buffer.append(')'); 373 } 374 }