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