001/*
002 * Copyright 2008-2020 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2008-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) 2008-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.unboundidds.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.LDAPException;
047import com.unboundid.ldap.sdk.ResultCode;
048import com.unboundid.util.Debug;
049import com.unboundid.util.NotMutable;
050import com.unboundid.util.NotNull;
051import com.unboundid.util.Nullable;
052import com.unboundid.util.StaticUtils;
053import com.unboundid.util.ThreadSafety;
054import com.unboundid.util.ThreadSafetyLevel;
055import com.unboundid.util.Validator;
056
057import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*;
058
059
060
061/**
062 * This class provides an implementation of the end interactive transaction
063 * extended request.  It may be used to either commit or abort a transaction
064 * that was created using the start interactive transaction request.  See the
065 * documentation in the {@link StartInteractiveTransactionExtendedRequest} for
066 * an example of processing an interactive transaction.
067 * <BR>
068 * <BLOCKQUOTE>
069 *   <B>NOTE:</B>  This class, and other classes within the
070 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
071 *   supported for use against Ping Identity, UnboundID, and
072 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
073 *   for proprietary functionality or for external specifications that are not
074 *   considered stable or mature enough to be guaranteed to work in an
075 *   interoperable way with other types of LDAP servers.
076 * </BLOCKQUOTE>
077 */
078@NotMutable()
079@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
080public final class EndInteractiveTransactionExtendedRequest
081       extends ExtendedRequest
082{
083  /**
084   * The OID (1.3.6.1.4.1.30221.2.6.4) for the end interactive transaction
085   * extended request.
086   */
087  @NotNull public static final String END_INTERACTIVE_TRANSACTION_REQUEST_OID =
088       "1.3.6.1.4.1.30221.2.6.4";
089
090
091
092  /**
093   * The BER type for the {@code txnID} element of the request.
094   */
095  private static final byte TYPE_TXN_ID = (byte) 0x80;
096
097
098
099  /**
100   * The BER type for the {@code commit} element of the request.
101   */
102  private static final byte TYPE_COMMIT = (byte) 0x81;
103
104
105
106  /**
107   * The serial version UID for this serializable class.
108   */
109  private static final long serialVersionUID = -7404929482337917353L;
110
111
112
113  // The transaction ID for the associated transaction.
114  @NotNull private final ASN1OctetString transactionID;
115
116  // Indicates whether to commit or abort the associated transaction.
117  private final boolean commit;
118
119
120
121  /**
122   * Creates a new end interactive transaction extended request with the
123   * provided information.
124   *
125   * @param  transactionID  The transaction ID for the transaction to commit or
126   *                        abort.  It must not be {@code null}.
127   * @param  commit         {@code true} if the transaction should be committed,
128   *                        or {@code false} if the transaction should be
129   *                        aborted.
130   */
131  public EndInteractiveTransactionExtendedRequest(
132              @NotNull final ASN1OctetString transactionID,
133              final boolean commit)
134  {
135    this(transactionID, commit, null);
136  }
137
138
139
140  /**
141   * Creates a new end interactive transaction extended request with the
142   * provided information.
143   *
144   * @param  transactionID  The transaction ID for the transaction to commit or
145   *                        abort.  It must not be {@code null}.
146   * @param  commit         {@code true} if the transaction should be committed,
147   *                        or {@code false} if the transaction should be
148   *                        aborted.
149   * @param  controls       The set of controls to include in the request.
150   */
151  public EndInteractiveTransactionExtendedRequest(
152              @NotNull final ASN1OctetString transactionID,
153              final boolean commit,
154              @Nullable final Control[] controls)
155  {
156    super(END_INTERACTIVE_TRANSACTION_REQUEST_OID,
157          encodeValue(transactionID, commit),
158          controls);
159
160    this.transactionID = transactionID;
161    this.commit        = commit;
162  }
163
164
165
166  /**
167   * Creates a new end interactive transaction extended request from the
168   * provided generic extended request.
169   *
170   * @param  extendedRequest  The generic extended request to use to create this
171   *                          end interactive transaction extended request.
172   *
173   * @throws  LDAPException  If a problem occurs while decoding the request.
174   */
175  public EndInteractiveTransactionExtendedRequest(
176              @NotNull final ExtendedRequest extendedRequest)
177         throws LDAPException
178  {
179    super(extendedRequest);
180
181    final ASN1OctetString value = extendedRequest.getValue();
182    if (value == null)
183    {
184      throw new LDAPException(ResultCode.DECODING_ERROR,
185                              ERR_END_INT_TXN_REQUEST_NO_VALUE.get());
186    }
187
188    ASN1OctetString txnID = null;
189    boolean shouldCommit = true;
190    try
191    {
192      final ASN1Element valueElement = ASN1Element.decode(value.getValue());
193      final ASN1Element[] elements =
194           ASN1Sequence.decodeAsSequence(valueElement).elements();
195
196      for (final ASN1Element e : elements)
197      {
198        if (e.getType() == TYPE_TXN_ID)
199        {
200          txnID = ASN1OctetString.decodeAsOctetString(e);
201        }
202        else if (e.getType() == TYPE_COMMIT)
203        {
204          shouldCommit = ASN1Boolean.decodeAsBoolean(e).booleanValue();
205        }
206        else
207        {
208          throw new LDAPException(ResultCode.DECODING_ERROR,
209               ERR_END_INT_TXN_REQUEST_INVALID_TYPE.get(
210                    StaticUtils.toHex(e.getType())));
211        }
212      }
213    }
214    catch (final LDAPException le)
215    {
216      Debug.debugException(le);
217      throw le;
218    }
219    catch (final Exception e)
220    {
221      Debug.debugException(e);
222      throw new LDAPException(ResultCode.DECODING_ERROR,
223                              ERR_END_INT_TXN_REQUEST_CANNOT_DECODE.get(e), e);
224    }
225
226    if (txnID == null)
227    {
228      throw new LDAPException(ResultCode.DECODING_ERROR,
229                              ERR_END_INT_TXN_REQUEST_NO_TXN_ID.get());
230    }
231
232    transactionID = txnID;
233    commit        = shouldCommit;
234  }
235
236
237
238  /**
239   * Generates the value to include in this extended request.
240   *
241   * @param  transactionID  The transaction ID for the transaction to commit or
242   *                        abort.  It must not be {@code null}.
243   * @param  commit         {@code true} if the transaction should be committed,
244   *                        or {@code false} if the transaction should be
245   *                        aborted.
246   *
247   * @return  The ASN.1 octet string containing the encoded request value.
248   */
249  @NotNull()
250  private static ASN1OctetString encodeValue(
251               @NotNull final ASN1OctetString transactionID,
252               final boolean commit)
253  {
254    Validator.ensureNotNull(transactionID);
255
256    final ASN1Element[] valueElements;
257    if (commit)
258    {
259      valueElements = new ASN1Element[]
260      {
261        new ASN1OctetString(TYPE_TXN_ID, transactionID.getValue())
262      };
263    }
264    else
265    {
266      valueElements = new ASN1Element[]
267      {
268        new ASN1OctetString(TYPE_TXN_ID, transactionID.getValue()),
269        new ASN1Boolean(TYPE_COMMIT, commit)
270      };
271    }
272
273    return new ASN1OctetString(new ASN1Sequence(valueElements).encode());
274  }
275
276
277
278  /**
279   * Retrieves the transaction ID for the transaction to commit or abort.
280   *
281   * @return  The transaction ID for the transaction to commit or abort.
282   */
283  @NotNull()
284  public ASN1OctetString getTransactionID()
285  {
286    return transactionID;
287  }
288
289
290
291  /**
292   * Indicates whether the transaction should be committed or aborted.
293   *
294   * @return  {@code true} if the transaction should be committed, or
295   *          {@code false} if it should be aborted.
296   */
297  public boolean commit()
298  {
299    return commit;
300  }
301
302
303
304  /**
305   * {@inheritDoc}
306   */
307  @Override()
308  @NotNull()
309  public EndInteractiveTransactionExtendedRequest duplicate()
310  {
311    return duplicate(getControls());
312  }
313
314
315
316  /**
317   * {@inheritDoc}
318   */
319  @Override()
320  @NotNull()
321  public EndInteractiveTransactionExtendedRequest duplicate(
322              @Nullable final Control[] controls)
323  {
324    final EndInteractiveTransactionExtendedRequest r =
325         new EndInteractiveTransactionExtendedRequest(transactionID, commit,
326              controls);
327    r.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
328    return r;
329  }
330
331
332
333  /**
334   * {@inheritDoc}
335   */
336  @Override()
337  @NotNull()
338  public String getExtendedRequestName()
339  {
340    return INFO_EXTENDED_REQUEST_NAME_END_INTERACTIVE_TXN.get();
341  }
342
343
344
345  /**
346   * {@inheritDoc}
347   */
348  @Override()
349  public void toString(@NotNull final StringBuilder buffer)
350  {
351    buffer.append("EndInteractiveTransactionExtendedRequest(transactionID='");
352    buffer.append(transactionID.stringValue());
353    buffer.append("', commit=");
354    buffer.append(commit);
355
356    final Control[] controls = getControls();
357    if (controls.length > 0)
358    {
359      buffer.append("controls={");
360      for (int i=0; i < controls.length; i++)
361      {
362        if (i > 0)
363        {
364          buffer.append(", ");
365        }
366
367        buffer.append(controls[i]);
368      }
369      buffer.append('}');
370    }
371
372    buffer.append(')');
373  }
374}