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