001/*
002 * Copyright 2010-2022 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2010-2022 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) 2010-2022 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.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.TransactionSpecificationRequestControl;
047import com.unboundid.util.NotMutable;
048import com.unboundid.util.NotNull;
049import com.unboundid.util.Nullable;
050import com.unboundid.util.ThreadSafety;
051import com.unboundid.util.ThreadSafetyLevel;
052
053import static com.unboundid.ldap.sdk.extensions.ExtOpMessages.*;
054
055
056
057/**
058 * This class provides an implementation of the start transaction extended
059 * request as defined in
060 * <A HREF="http://www.ietf.org/rfc/rfc5805.txt">RFC 5805</A>.  It may be used
061 * to begin a transaction that allows multiple write operations to be processed
062 * as a single atomic unit.  The {@link StartTransactionExtendedResult} that is
063 * returned will include a transaction ID.  For each operation that is performed
064 * as part of the transaction, this transaction ID should be included in the
065 * corresponding request through the
066 * {@link TransactionSpecificationRequestControl}.  Finally, after all requests
067 * for the transaction have been submitted to the server, the
068 * {@link EndTransactionExtendedRequest} should be used to commit that
069 * transaction, or it may also be used to abort the transaction if it is decided
070 * that it is no longer needed.
071 * <BR><BR>
072 * <H2>Example</H2>
073 * The following example demonstrates the process for using LDAP  transactions.
074 * It will modify two different entries as a single atomic unit.
075 * <PRE>
076 * // Use the start transaction extended operation to begin a transaction.
077 * StartTransactionExtendedResult startTxnResult;
078 * try
079 * {
080 *   startTxnResult = (StartTransactionExtendedResult)
081 *        connection.processExtendedOperation(
082 *             new StartTransactionExtendedRequest());
083 *   // This doesn't necessarily mean that the operation was successful, since
084 *   // some kinds of extended operations return non-success results under
085 *   // normal conditions.
086 * }
087 * catch (LDAPException le)
088 * {
089 *   // For an extended operation, this generally means that a problem was
090 *   // encountered while trying to send the request or read the result.
091 *   startTxnResult = new StartTransactionExtendedResult(
092 *        new ExtendedResult(le));
093 * }
094 * LDAPTestUtils.assertResultCodeEquals(startTxnResult, ResultCode.SUCCESS);
095 * ASN1OctetString txnID = startTxnResult.getTransactionID();
096 *
097 *
098 * // At this point, we have a transaction available for use.  If any problem
099 * // arises, we want to ensure that the transaction is aborted, so create a
100 * // try block to process the operations and a finally block to commit or
101 * // abort the transaction.
102 * boolean commit = false;
103 * try
104 * {
105 *   // Create and process a modify operation to update a first entry as part
106 *   // of the transaction.  Make sure to include the transaction specification
107 *   // control in the request to indicate that it should be part of the
108 *   // transaction.
109 *   ModifyRequest firstModifyRequest = new ModifyRequest(
110 *        "cn=first,dc=example,dc=com",
111 *        new Modification(ModificationType.REPLACE, "description", "first"));
112 *   firstModifyRequest.addControl(
113 *        new TransactionSpecificationRequestControl(txnID));
114 *   LDAPResult firstModifyResult;
115 *   try
116 *   {
117 *     firstModifyResult = connection.modify(firstModifyRequest);
118 *   }
119 *   catch (LDAPException le)
120 *   {
121 *     firstModifyResult = le.toLDAPResult();
122 *   }
123 *   LDAPTestUtils.assertResultCodeEquals(firstModifyResult,
124 *        ResultCode.SUCCESS);
125 *
126 *   // Perform a second modify operation as part of the transaction.
127 *   ModifyRequest secondModifyRequest = new ModifyRequest(
128 *        "cn=second,dc=example,dc=com",
129 *        new Modification(ModificationType.REPLACE, "description", "second"));
130 *   secondModifyRequest.addControl(
131 *        new TransactionSpecificationRequestControl(txnID));
132 *   LDAPResult secondModifyResult;
133 *   try
134 *   {
135 *     secondModifyResult = connection.modify(secondModifyRequest);
136 *   }
137 *   catch (LDAPException le)
138 *   {
139 *     secondModifyResult = le.toLDAPResult();
140 *   }
141 *   LDAPTestUtils.assertResultCodeEquals(secondModifyResult,
142 *        ResultCode.SUCCESS);
143 *
144 *   // If we've gotten here, then all writes have been processed successfully
145 *   // and we can indicate that the transaction should be committed rather
146 *   // than aborted.
147 *   commit = true;
148 * }
149 * finally
150 * {
151 *   // Commit or abort the transaction.
152 *   EndTransactionExtendedResult endTxnResult;
153 *   try
154 *   {
155 *     endTxnResult = (EndTransactionExtendedResult)
156 *          connection.processExtendedOperation(
157 *               new EndTransactionExtendedRequest(txnID, commit));
158 *   }
159 *   catch (LDAPException le)
160 *   {
161 *     endTxnResult = new EndTransactionExtendedResult(new ExtendedResult(le));
162 *   }
163 *   LDAPTestUtils.assertResultCodeEquals(endTxnResult, ResultCode.SUCCESS);
164 * }
165 * </PRE>
166 */
167@NotMutable()
168@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
169public 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  @NotNull public static final String START_TRANSACTION_REQUEST_OID =
176       "1.3.6.1.1.21.1";
177
178
179  /**
180   * The serial version UID for this serializable class.
181   */
182  private static final long serialVersionUID = 7382735226826929629L;
183
184
185
186  /**
187   * Creates a new start transaction extended request.
188   */
189  public StartTransactionExtendedRequest()
190  {
191    super(START_TRANSACTION_REQUEST_OID);
192  }
193
194
195
196  /**
197   * Creates a new start transaction extended request.
198   *
199   * @param  controls  The set of controls to include in the request.
200   */
201  public StartTransactionExtendedRequest(@Nullable final Control[] controls)
202  {
203    super(START_TRANSACTION_REQUEST_OID, controls);
204  }
205
206
207
208  /**
209   * Creates a new start transaction extended request from the provided generic
210   * extended request.
211   *
212   * @param  extendedRequest  The generic extended request to use to create this
213   *                          start transaction extended request.
214   *
215   * @throws  LDAPException  If a problem occurs while decoding the request.
216   */
217  public StartTransactionExtendedRequest(
218              @NotNull final ExtendedRequest extendedRequest)
219         throws LDAPException
220  {
221    super(extendedRequest);
222
223    if (extendedRequest.hasValue())
224    {
225      throw new LDAPException(ResultCode.DECODING_ERROR,
226           ERR_START_TXN_REQUEST_HAS_VALUE.get());
227    }
228  }
229
230
231
232  /**
233   * {@inheritDoc}
234   */
235  @Override()
236  @NotNull()
237  public StartTransactionExtendedResult process(
238              @NotNull final LDAPConnection connection, final int depth)
239         throws LDAPException
240  {
241    final ExtendedResult extendedResponse = super.process(connection, depth);
242    return new StartTransactionExtendedResult(extendedResponse);
243  }
244
245
246
247  /**
248   * {@inheritDoc}
249   */
250  @Override()
251  @NotNull()
252  public StartTransactionExtendedRequest duplicate()
253  {
254    return duplicate(getControls());
255  }
256
257
258
259  /**
260   * {@inheritDoc}
261   */
262  @Override()
263  @NotNull()
264  public StartTransactionExtendedRequest duplicate(
265              @Nullable final Control[] controls)
266  {
267    final StartTransactionExtendedRequest r =
268         new StartTransactionExtendedRequest(controls);
269    r.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
270    return r;
271  }
272
273
274
275  /**
276   * {@inheritDoc}
277   */
278  @Override()
279  @NotNull()
280  public String getExtendedRequestName()
281  {
282    return INFO_EXTENDED_REQUEST_NAME_START_TXN.get();
283  }
284
285
286
287  /**
288   * {@inheritDoc}
289   */
290  @Override()
291  public void toString(@NotNull final StringBuilder buffer)
292  {
293    buffer.append("StartTransactionExtendedRequest(");
294
295    final Control[] controls = getControls();
296    if (controls.length > 0)
297    {
298      buffer.append("controls={");
299      for (int i=0; i < controls.length; i++)
300      {
301        if (i > 0)
302        {
303          buffer.append(", ");
304        }
305
306        buffer.append(controls[i]);
307      }
308      buffer.append('}');
309    }
310
311    buffer.append(')');
312  }
313}