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 }