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}