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