001 /*
002 * Copyright 2010-2013 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2010-2013 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.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.TransactionSpecificationRequestControl;
032 import com.unboundid.util.NotMutable;
033 import com.unboundid.util.ThreadSafety;
034 import com.unboundid.util.ThreadSafetyLevel;
035
036 import static com.unboundid.ldap.sdk.extensions.ExtOpMessages.*;
037
038
039
040 /**
041 * This class provides an implementation of the start transaction extended
042 * request as defined in
043 * <A HREF="http://www.ietf.org/rfc/rfc5805.txt">RFC 5805</A>. It may be used
044 * to begin a transaction that allows multiple write operations to be processed
045 * as a single atomic unit. The {@link StartTransactionExtendedResult} that is
046 * returned will include a transaction ID. For each operation that is performed
047 * as part of the transaction, this transaction ID should be included in the
048 * corresponding request through the
049 * {@link TransactionSpecificationRequestControl}. Finally, after all requests
050 * for the transaction have been submitted to the server, the
051 * {@link EndTransactionExtendedRequest} should be used to commit that
052 * transaction, or it may also be used to abort the transaction if it is decided
053 * that it is no longer needed.
054 * <BR><BR>
055 * <H2>Example</H2>
056 * The following example demonstrates the process for using LDAP transactions.
057 * It will modify two different entries as a single atomic unit. In each case,
058 * it will use the post-read control to retrieve a copy of the updated entry.
059 * <PRE>
060 * // Send the start transaction operation and get the transaction ID.
061 * StartTransactionExtendedRequest startTxnRequest =
062 * new StartTransactionExtendedRequest();
063 * StartTransactionExtendedResult startTxnResult =
064 * (StartTransactionExtendedResult)
065 * connection.processExtendedOperation(startTxnRequest);
066 * if (startTxnResult.getResultCode() != ResultCode.SUCCESS)
067 * {
068 * throw new LDAPException(startTxnResult);
069 * }
070 * ASN1OctetString txnID = startTxnResult.getTransactionID();
071 *
072 * // At this point, we have a transaction available for use. If any error
073 * // occurs, we will want to make sure that the transaction is aborted, so
074 * // use a try/finally block to handle that.
075 * boolean shouldAbort = true;
076 * try
077 * {
078 * // Create and send the first modify request as part of the transaction.
079 * // Make sure to include the transaction specification control and the
080 * // post-read request control in the modify request.
081 * ModifyRequest modifyRequest1 = new ModifyRequest(
082 * "cn=first,dc=example,dc=com",
083 * new Modification(ModificationType.REPLACE, "description", "first");
084 * modifyRequest1.addControl(new TransactionSpecificationControl(txnID));
085 * modifyRequest1.addControl(new PostReadRequestControl());
086 * LDAPResult modifyResult1 = connection.modify(modifyRequest1);
087 *
088 * // Create and send the second modify request as part of the transaction.
089 * // Again, make sure to include the appropriate controls in the request.
090 * ModifyRequest modifyRequest2 = new ModifyRequest(
091 * "cn=second,dc=example,dc=com",
092 * new Modification(ModificationType.REPLACE, "description", "second");
093 * modifyRequest2.addControl(new TransactionSpecificationControl(txnID));
094 * modifyRequest2.addControl(new PostReadRequestControl());
095 * LDAPResult modifyResult2 = connection.modify(modifyRequest1);
096 *
097 * // Now we're ready to commit, which we can do with the end transaction
098 * // request with the commit flag set to true.
099 * EndTransactionExtendedRequest commitRequest =
100 * new EndTransactionExtendedRequest(txnID, true);
101 * EndTransactionExtendedResult commitResult =
102 * (EndTransactionExtendedResult)
103 * connection.processExtendedOperation(commitRequest);
104 * if (commitResult.getResultCode() == ResultCode.SUCCESS)
105 * {
106 * System.out.println("The transaction was committed successfully.");
107 *
108 * // Everything was successful, so we don't need to abort anything.
109 * shouldAbort = false;
110 *
111 * // Get the post-read response control for the first modify operation.
112 * // It's the same process for the second, but this example is already
113 * // long enough so we'll skip it.
114 * Control[] controls = commitResult.getOperationResponseControls(
115 * modifyResult1.getMessageID());
116 * if (controls != null)
117 * {
118 * for (Control c : controls)
119 * {
120 * if (c instanceof PostReadResponseControl)
121 * {
122 * PostReadResponseControl postReadResponse =
123 * (PostReadResponseControl) c;
124 * System.out.println("First entry after the modification:");
125 * System.out.println(postReadResponse.getEntry().toLDIFString());
126 * }
127 * }
128 * }
129 * }
130 * else
131 * {
132 * // The transaction failed for some reason. The response should tell us
133 * // whether it failed because of one of the operations.
134 * int failedOpMessageID = commitResult.getFailedOpMessageID();
135 * if (failedOpMessageID == modifyResult1.getMessageID())
136 * {
137 * System.err.println("The transaction failed because of a failure " +
138 * "encountered while processing the first modification.");
139 * }
140 * else if (failedOpMessageID == modifyResult2.getMessageID())
141 * {
142 * System.err.println("The transaction failed because of a failure " +
143 * "encountered while processing the second modification.");
144 * }
145 * else
146 * {
147 * System.err.println("The transaction failed for some reason other " +
148 * "than either of the modify operations.");
149 * }
150 *
151 * throw new LDAPException(commitResult);
152 * }
153 * }
154 * finally
155 * {
156 * if (shouldAbort)
157 * {
158 * // Setting the commit flag to false in the end transaction request will
159 * // will cause the transaction to be aborted rather than committed.
160 * EndTransactionExtendedRequest abortRequest =
161 * new EndTransactionExtendedRequest(txnID, false);
162 * connection.processExtendedOperation(abortRequest);
163 * }
164 * }
165 * </PRE>
166 */
167 @NotMutable()
168 @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
169 public final class StartTransactionExtendedRequest
170 extends ExtendedRequest
171 {
172 /**
173 * The OID (1.3.6.1.1.21.1) for the start transaction extended request.
174 */
175 public static final String START_TRANSACTION_REQUEST_OID = "1.3.6.1.1.21.1";
176
177
178 /**
179 * The serial version UID for this serializable class.
180 */
181 private static final long serialVersionUID = 7382735226826929629L;
182
183
184
185 // This is an ugly hack to prevent checkstyle from complaining about imports
186 // for classes that are needed by javadoc @link elements but aren't otherwise
187 // used in the class. It appears that checkstyle does not recognize the use
188 // of these classes in javadoc @link elements so we must ensure that they are
189 // referenced elsewhere in the class to prevent checkstyle from complaining.
190 static
191 {
192 final TransactionSpecificationRequestControl c = null;
193 }
194
195
196
197 /**
198 * Creates a new start transaction extended request.
199 */
200 public StartTransactionExtendedRequest()
201 {
202 super(START_TRANSACTION_REQUEST_OID);
203 }
204
205
206
207 /**
208 * Creates a new start transaction extended request.
209 *
210 * @param controls The set of controls to include in the request.
211 */
212 public StartTransactionExtendedRequest(final Control[] controls)
213 {
214 super(START_TRANSACTION_REQUEST_OID, controls);
215 }
216
217
218
219 /**
220 * Creates a new start transaction extended request from the provided generic
221 * extended request.
222 *
223 * @param extendedRequest The generic extended request to use to create this
224 * start transaction extended request.
225 *
226 * @throws LDAPException If a problem occurs while decoding the request.
227 */
228 public StartTransactionExtendedRequest(final ExtendedRequest extendedRequest)
229 throws LDAPException
230 {
231 super(extendedRequest);
232
233 if (extendedRequest.hasValue())
234 {
235 throw new LDAPException(ResultCode.DECODING_ERROR,
236 ERR_START_TXN_REQUEST_HAS_VALUE.get());
237 }
238 }
239
240
241
242 /**
243 * {@inheritDoc}
244 */
245 @Override()
246 public StartTransactionExtendedResult process(
247 final LDAPConnection connection, final int depth)
248 throws LDAPException
249 {
250 final ExtendedResult extendedResponse = super.process(connection, depth);
251 return new StartTransactionExtendedResult(extendedResponse);
252 }
253
254
255
256 /**
257 * {@inheritDoc}
258 */
259 @Override()
260 public StartTransactionExtendedRequest duplicate()
261 {
262 return duplicate(getControls());
263 }
264
265
266
267 /**
268 * {@inheritDoc}
269 */
270 @Override()
271 public StartTransactionExtendedRequest duplicate(final Control[] controls)
272 {
273 final StartTransactionExtendedRequest r =
274 new StartTransactionExtendedRequest(controls);
275 r.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
276 return r;
277 }
278
279
280
281 /**
282 * {@inheritDoc}
283 */
284 @Override()
285 public String getExtendedRequestName()
286 {
287 return INFO_EXTENDED_REQUEST_NAME_START_TXN.get();
288 }
289
290
291
292 /**
293 * {@inheritDoc}
294 */
295 @Override()
296 public void toString(final StringBuilder buffer)
297 {
298 buffer.append("StartTransactionExtendedRequest(");
299
300 final Control[] controls = getControls();
301 if (controls.length > 0)
302 {
303 buffer.append("controls={");
304 for (int i=0; i < controls.length; i++)
305 {
306 if (i > 0)
307 {
308 buffer.append(", ");
309 }
310
311 buffer.append(controls[i]);
312 }
313 buffer.append('}');
314 }
315
316 buffer.append(')');
317 }
318 }