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