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