001/*
002 * Copyright 2010-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2010-2024 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-2024 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.asn1.ASN1Boolean;
041import com.unboundid.asn1.ASN1Element;
042import com.unboundid.asn1.ASN1OctetString;
043import com.unboundid.asn1.ASN1Sequence;
044import com.unboundid.ldap.sdk.Control;
045import com.unboundid.ldap.sdk.ExtendedRequest;
046import com.unboundid.ldap.sdk.ExtendedResult;
047import com.unboundid.ldap.sdk.LDAPConnection;
048import com.unboundid.ldap.sdk.LDAPException;
049import com.unboundid.ldap.sdk.ResultCode;
050import com.unboundid.util.Debug;
051import com.unboundid.util.NotMutable;
052import com.unboundid.util.NotNull;
053import com.unboundid.util.Nullable;
054import com.unboundid.util.ThreadSafety;
055import com.unboundid.util.ThreadSafetyLevel;
056import com.unboundid.util.Validator;
057
058import static com.unboundid.ldap.sdk.extensions.ExtOpMessages.*;
059
060
061
062/**
063 * This class provides an implementation of the end transaction extended
064 * request as defined in
065 * <A HREF="http://www.ietf.org/rfc/rfc5805.txt">RFC 5805</A>.  It may be used
066 * to either commit or abort a transaction that was created using the start
067 * transaction request.  See the documentation for the
068 * {@link StartTransactionExtendedRequest} class for an example of processing an
069 * LDAP transaction.
070 */
071@NotMutable()
072@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
073public final class EndTransactionExtendedRequest
074       extends ExtendedRequest
075{
076  /**
077   * The OID (1.3.6.1.1.21.3) for the end transaction extended request.
078   */
079  @NotNull public static final String END_TRANSACTION_REQUEST_OID =
080       "1.3.6.1.1.21.3";
081
082
083
084  /**
085   * The serial version UID for this serializable class.
086   */
087  private static final long serialVersionUID = -7135468264026410702L;
088
089
090
091  // The transaction ID for the associated transaction.
092  @NotNull private final ASN1OctetString transactionID;
093
094  // Indicates whether to commit or abort the associated transaction.
095  private final boolean commit;
096
097
098
099  /**
100   * Creates a new end transaction extended request with the provided
101   * information.
102   *
103   * @param  transactionID  The transaction ID for the transaction to commit or
104   *                        abort.  It must not be {@code null}.
105   * @param  commit         {@code true} if the transaction should be committed,
106   *                        or {@code false} if the transaction should be
107   *                        aborted.
108   * @param  controls       The set of controls to include in the request.
109   */
110  public EndTransactionExtendedRequest(
111              @NotNull final ASN1OctetString transactionID,
112              final boolean commit,
113              @Nullable final Control... controls)
114  {
115    super(END_TRANSACTION_REQUEST_OID, encodeValue(transactionID, commit),
116          controls);
117
118    this.transactionID = transactionID;
119    this.commit        = commit;
120  }
121
122
123
124  /**
125   * Creates a new end transaction extended request from the provided generic
126   * extended request.
127   *
128   * @param  extendedRequest  The generic extended request to use to create this
129   *                          end transaction extended request.
130   *
131   * @throws  LDAPException  If a problem occurs while decoding the request.
132   */
133  public EndTransactionExtendedRequest(
134              @NotNull final ExtendedRequest extendedRequest)
135         throws LDAPException
136  {
137    super(extendedRequest);
138
139    final ASN1OctetString value = extendedRequest.getValue();
140    if (value == null)
141    {
142      throw new LDAPException(ResultCode.DECODING_ERROR,
143           ERR_END_TXN_REQUEST_NO_VALUE.get());
144    }
145
146    try
147    {
148      final ASN1Element valueElement = ASN1Element.decode(value.getValue());
149      final ASN1Element[] elements =
150           ASN1Sequence.decodeAsSequence(valueElement).elements();
151      if (elements.length == 1)
152      {
153        commit        = true;
154        transactionID = ASN1OctetString.decodeAsOctetString(elements[0]);
155      }
156      else
157      {
158        commit        = ASN1Boolean.decodeAsBoolean(elements[0]).booleanValue();
159        transactionID = ASN1OctetString.decodeAsOctetString(elements[1]);
160      }
161    }
162    catch (final Exception e)
163    {
164      Debug.debugException(e);
165      throw new LDAPException(ResultCode.DECODING_ERROR,
166           ERR_END_TXN_REQUEST_CANNOT_DECODE.get(e), e);
167    }
168  }
169
170
171
172  /**
173   * Generates the value to include in this extended request.
174   *
175   * @param  transactionID  The transaction ID for the transaction to commit or
176   *                        abort.  It must not be {@code null}.
177   * @param  commit         {@code true} if the transaction should be committed,
178   *                        or {@code false} if the transaction should be
179   *                        aborted.
180   *
181   * @return  The ASN.1 octet string containing the encoded request value.
182   */
183  @NotNull()
184  private static ASN1OctetString encodeValue(
185                      @NotNull final ASN1OctetString transactionID,
186                      final boolean commit)
187  {
188    Validator.ensureNotNull(transactionID);
189
190    final ASN1Element[] valueElements;
191    if (commit)
192    {
193      valueElements = new ASN1Element[]
194      {
195        transactionID
196      };
197    }
198    else
199    {
200      valueElements = new ASN1Element[]
201      {
202        new ASN1Boolean(commit),
203        transactionID
204      };
205    }
206
207    return new ASN1OctetString(new ASN1Sequence(valueElements).encode());
208  }
209
210
211
212  /**
213   * Retrieves the transaction ID for the transaction to commit or abort.
214   *
215   * @return  The transaction ID for the transaction to commit or abort.
216   */
217  @NotNull()
218  public ASN1OctetString getTransactionID()
219  {
220    return transactionID;
221  }
222
223
224
225  /**
226   * Indicates whether the transaction should be committed or aborted.
227   *
228   * @return  {@code true} if the transaction should be committed, or
229   *          {@code false} if it should be aborted.
230   */
231  public boolean commit()
232  {
233    return commit;
234  }
235
236
237
238  /**
239   * {@inheritDoc}
240   */
241  @Override()
242  @NotNull()
243  public EndTransactionExtendedResult process(
244              @NotNull final LDAPConnection connection, final int depth)
245         throws LDAPException
246  {
247    final ExtendedResult extendedResponse = super.process(connection, depth);
248    return new EndTransactionExtendedResult(extendedResponse);
249  }
250
251
252
253  /**
254   * {@inheritDoc}
255   */
256  @Override()
257  @NotNull()
258  public EndTransactionExtendedRequest duplicate()
259  {
260    return duplicate(getControls());
261  }
262
263
264
265  /**
266   * {@inheritDoc}
267   */
268  @Override()
269  @NotNull()
270  public EndTransactionExtendedRequest duplicate(
271              @Nullable final Control[] controls)
272  {
273    final EndTransactionExtendedRequest r =
274         new EndTransactionExtendedRequest(transactionID, commit, controls);
275    r.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
276    r.setIntermediateResponseListener(getIntermediateResponseListener());
277    r.setReferralDepth(getReferralDepth());
278    r.setReferralConnector(getReferralConnectorInternal());
279    return r;
280  }
281
282
283
284  /**
285   * {@inheritDoc}
286   */
287  @Override()
288  @NotNull()
289  public String getExtendedRequestName()
290  {
291    return INFO_EXTENDED_REQUEST_NAME_END_TXN.get();
292  }
293
294
295
296  /**
297   * {@inheritDoc}
298   */
299  @Override()
300  public void toString(@NotNull final StringBuilder buffer)
301  {
302    buffer.append("EndTransactionExtendedRequest(transactionID='");
303    buffer.append(transactionID.stringValue());
304    buffer.append("', commit=");
305    buffer.append(commit);
306
307    final Control[] controls = getControls();
308    if (controls.length > 0)
309    {
310      buffer.append("controls={");
311      for (int i=0; i < controls.length; i++)
312      {
313        if (i > 0)
314        {
315          buffer.append(", ");
316        }
317
318        buffer.append(controls[i]);
319      }
320      buffer.append('}');
321    }
322
323    buffer.append(')');
324  }
325}