001/*
002 * Copyright 2014-2025 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2014-2025 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) 2014-2025 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.controls;
037
038
039
040import java.util.ArrayList;
041
042import com.unboundid.asn1.ASN1Boolean;
043import com.unboundid.asn1.ASN1Element;
044import com.unboundid.asn1.ASN1Enumerated;
045import com.unboundid.asn1.ASN1Integer;
046import com.unboundid.asn1.ASN1Long;
047import com.unboundid.asn1.ASN1OctetString;
048import com.unboundid.asn1.ASN1Sequence;
049import com.unboundid.ldap.sdk.Control;
050import com.unboundid.ldap.sdk.LDAPException;
051import com.unboundid.ldap.sdk.ResultCode;
052import com.unboundid.util.Debug;
053import com.unboundid.util.NotMutable;
054import com.unboundid.util.NotNull;
055import com.unboundid.util.Nullable;
056import com.unboundid.util.StaticUtils;
057import com.unboundid.util.ThreadSafety;
058import com.unboundid.util.ThreadSafetyLevel;
059import com.unboundid.util.Validator;
060
061import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*;
062
063
064
065/**
066 * This class provides a request control that can be used to specify a number of
067 * settings used for any database transaction that may be associated with the
068 * associated request.  It may be included in an end transaction extended
069 * request or an atomic multi-update extended request (it is not supported for
070 * use in non-atomic multi-update requests).
071 * <BR>
072 * <BLOCKQUOTE>
073 *   <B>NOTE:</B>  This class, and other classes within the
074 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
075 *   supported for use against Ping Identity, UnboundID, and
076 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
077 *   for proprietary functionality or for external specifications that are not
078 *   considered stable or mature enough to be guaranteed to work in an
079 *   interoperable way with other types of LDAP servers.
080 * </BLOCKQUOTE>
081 * <BR>
082 * This control has an OID of 1.3.6.1.4.1.30221.2.5.38.  It may have a
083 * criticality of either {@code true} (in which case the server will reject the
084 * associated operation if this control is not recognized) or {@code false} (in
085 * which case the server will ignore this control if it is not recognized).  It
086 * must have a value with the following encoding:
087 * <PRE>
088 *   TransactionSettingsRequestValue ::= SEQUENCE {
089 *        transactionName              [0] OCTET STRING OPTIONAL,
090 *        commitDurability             [1] ENUMERATED {
091 *             nonSynchronous                   (0),
092 *             partiallySynchronous             (1),
093 *             fullySynchronous                 (2),
094 *             ... } OPTIONAL,
095 *        backendLockBehavior          [2] ENUMERATED {
096 *             doNotAcquire                     (0),
097 *             acquireAfterRetries              (1),
098 *             acquireBeforeRetries             (2),
099 *             acquireBeforeInitialAttempt      (3),
100 *             ... } OPTIONAL,
101 *        backendLockTimeoutMillis     [3] INTEGER OPTIONAL,
102 *        retryAttempts                [4] INTEGER OPTIONAL,
103 *        txnLockTimeout               [5] SEQUENCE {
104 *             minTimeoutMillis                 INTEGER,
105 *             maxTimeoutMillis                 INTEGER,
106 *             ... } OPTIONAL,
107 *        returnResponseControl        [6] BOOLEAN DEFAULT FALSE,
108 *        singleWriterLockBehavior     [7] ENUMERATED {
109 *             doNotAcquire                     (0),
110 *             acquireAfterRetries              (1),
111 *             acquireBeforeRetries             (2),
112 *             acquireBeforeInitialAttempt      (3),
113 *             ... } OPTIONAL,
114 *        scopedLockDetails            [8] SEQUENCE {
115 *             scopeIdentifier                  [9] OCTET STRING,
116 *             lockBehavior                     [10] ENUMERATED {
117 *                  doNotAcquire                          (0),
118 *                  acquireAfterRetries                   (1),
119 *                  acquireBeforeRetries                  (2),
120 *                  acquireBeforeInitialAttempt           (3),
121 *                  ... },
122 *             ... } OPTIONAL,
123 *        returnResponseControl        [11] BOOLEAN DEFAULT FALSE,
124 *        ... }
125 * </PRE>
126 */
127@NotMutable()
128@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
129public final class TransactionSettingsRequestControl
130       extends Control
131{
132  /**
133   * The OID (1.3.6.1.4.1.30221.2.5.38) for the undelete request control.
134   */
135  @NotNull public static final String TRANSACTION_SETTINGS_REQUEST_OID =
136       "1.3.6.1.4.1.30221.2.5.38";
137
138
139
140  /**
141   * The BER type for the value sequence element that specifies the name to use
142   * for the transaction.
143   */
144  private static final byte TYPE_TXN_NAME = (byte) 0x80;
145
146
147
148  /**
149   * The BER type for the value sequence element that specifies the commit
150   * durability to use.
151   */
152  private static final byte TYPE_COMMIT_DURABILITY = (byte) 0x81;
153
154
155
156  /**
157   * The BER type for the value sequence element that specifies the behavior
158   * to use with regard to acquiring the backend exclusive lock.
159   */
160  private static final byte TYPE_BACKEND_EXCLUSIVE_LOCK_BEHAVIOR = (byte) 0x82;
161
162
163
164  /**
165   * The BER type for the value sequence element that specifies the exclusive
166   * backend lock timeout.
167   */
168  private static final byte TYPE_BACKEND_LOCK_TIMEOUT = (byte) 0x83;
169
170
171
172  /**
173   * The BER type for the value sequence element that specifies the number of
174   * retry attempts.
175   */
176  private static final byte TYPE_RETRY_ATTEMPTS = (byte) 0x84;
177
178
179
180  /**
181   * The BER type for the value sequence element that specifies the minimum and
182   * maximum database lock timeout values.
183   */
184  private static final byte TYPE_TXN_LOCK_TIMEOUT = (byte) 0xA5;
185
186
187
188  /**
189   * The BER type for the value sequence element that indicates whether the
190   * contents of the control should be considered when the operation is
191   * replciated to other servers in the topology.
192   */
193  private static final byte TYPE_REPLICATE_CONTROL = (byte) 0x8B;
194
195
196
197  /**
198   * The BER type for the value sequence element that indicates whether to
199   * return a response control with transaction-related information about the
200   * processing of the associated operation.
201   */
202  private static final byte TYPE_RETURN_RESPONSE_CONTROL = (byte) 0x86;
203
204
205
206  /**
207   * The BER type for the value sequence element that specifies the behavior
208   * to use with regard to acquiring the backend single-writer lock.
209   */
210  private static final byte TYPE_SINGLE_WRITER_LOCK_BEHAVIOR = (byte) 0x87;
211
212
213
214  /**
215   * The BER type for the value sequence element that encapsulates information
216   * about the potential need to acquire a scoped lock during operation
217   * processing.
218   */
219  static final byte TYPE_SCOPED_LOCK_DETAILS = (byte) 0xA8;
220
221
222
223  /**
224   * The serial version UID for this serializable class.
225   */
226  private static final long serialVersionUID = -4749344077745581287L;
227
228
229
230  // Indicates whether the settings in this control should be considered when
231  // the operation is replicated to other servers in the topology.
232  private final boolean replicateControl;
233
234  // Indicates whether to return a response control.
235  private final boolean returnResponseControl;
236
237  // The number of times to retry if a lock conflict exception is encountered.
238  @Nullable private final Integer retryAttempts;
239
240  // The backend lock timeout, in milliseconds.
241  @Nullable private final Long backendLockTimeoutMillis;
242
243  // The maximum transaction lock timeout, in milliseconds.
244  @Nullable private final Long maxTxnLockTimeoutMillis;
245
246  // The minimum transaction lock timeout, in milliseconds.
247  @Nullable private final Long minTxnLockTimeoutMillis;
248
249  // The requested transaction name.
250  @Nullable private final String transactionName;
251
252  // The behavior to use with regard to requesting the backend exclusive lock.
253  @Nullable private final TransactionSettingsBackendLockBehavior
254       backendExclusiveLockBehavior;
255
256  // The behavior to use with regard to requesting the single-writer backend
257  // lock.
258  @Nullable private final TransactionSettingsBackendLockBehavior
259       singleWriterLockBehavior;
260
261  // The requested commit durability setting.
262  @Nullable private final TransactionSettingsCommitDurability commitDurability;
263
264  // Details about scoped lock usage.
265  @Nullable private final TransactionSettingsScopedLockDetails
266       scopedLockDetails;
267
268
269
270  /**
271   * Creates a new transaction settings request control with the provided
272   * information.
273   *
274   * @param  isCritical                Indicates whether the control should be
275   *                                   considered critical.
276   * @param  transactionName           The name to use for the transaction.  It
277   *                                   may be {@code null} if no
278   *                                   client-specified transaction name is
279   *                                   needed.  If a transaction name is
280   *                                   provided, it will be used purely for
281   *                                   informational and/or troubleshooting
282   *                                   purposes.
283   * @param  commitDurability          The durability level that should be used
284   *                                   when committing the associated
285   *                                   transaction.  It may be {@code null} if
286   *                                   the server-default durability level
287   *                                   should be used.
288   * @param  backendLockBehavior       The behavior that should be used with
289   *                                   regard to acquiring an exclusive lock for
290   *                                   processing in the target backend.  It may
291   *                                   be {@code null} if the server-default
292   *                                   backend lock behavior should be used.
293   * @param  backendLockTimeoutMillis  The maximum length of time in
294   *                                   milliseconds to spend attempting to
295   *                                   acquire an exclusive backend lock if it
296   *                                   is needed during any part of the
297   *                                   processing.  A value that of zero
298   *                                   indicates that no timeout should be
299   *                                   enforced.  It may be {@code null} if the
300   *                                   server will determine the backend lock
301   *                                   timeout that should be used.
302   * @param  retryAttempts             The number of times to retry the
303   *                                   associated operations in a new
304   *                                   transaction if the initial attempt fails.
305   *                                   If this is {@code null}, then the server
306   *                                   will determine the number of retry
307   *                                   attempts to make.  Note that depending on
308   *                                   the backend lock behavior, the server may
309   *                                   make one additional retry attempt if
310   *                                   necessary after acquiring an exclusive
311   *                                   backend lock.
312   * @param  minTxnLockTimeoutMillis   The minimum database lock timeout that
313   *                                   should be used for the associated
314   *                                   transaction.  If this is specified, then
315   *                                   the first attempt will use this lock
316   *                                   timeout, and subsequent attempts will use
317   *                                   a timeout value between this and the
318   *                                   maximum database lock timeout (which must
319   *                                   also be specified).  If this is
320   *                                   {@code null}, then the server will
321   *                                   determine the database lock timeout
322   *                                   settings to use.
323   * @param  maxTxnLockTimeoutMillis   The maximum database lock timeout that
324   *                                   should be used for the associated
325   *                                   transaction.  If this is specified, then
326   *                                   the minimum database lock timeout must
327   *                                   also be specified, and this value must be
328   *                                   greater than or equal to the minimum lock
329   *                                   timeout.  If this is {@code null}, then
330   *                                   the server will determine the database
331   *                                   lock timeout settings to use.
332   */
333  public TransactionSettingsRequestControl(final boolean isCritical,
334       @Nullable final String transactionName,
335       @Nullable final TransactionSettingsCommitDurability commitDurability,
336       @Nullable final TransactionSettingsBackendLockBehavior
337                                                backendLockBehavior,
338       @Nullable final Long backendLockTimeoutMillis,
339       @Nullable final Integer retryAttempts,
340       @Nullable final Long minTxnLockTimeoutMillis,
341       @Nullable final Long maxTxnLockTimeoutMillis)
342  {
343    this(isCritical, transactionName, commitDurability, backendLockBehavior,
344         backendLockTimeoutMillis, retryAttempts, minTxnLockTimeoutMillis,
345         maxTxnLockTimeoutMillis, false);
346  }
347
348
349
350  /**
351   * Creates a new transaction settings request control with the provided
352   * information.
353   *
354   * @param  isCritical                Indicates whether the control should be
355   *                                   considered critical.
356   * @param  transactionName           The name to use for the transaction.  It
357   *                                   may be {@code null} if no
358   *                                   client-specified transaction name is
359   *                                   needed.  If a transaction name is
360   *                                   provided, it will be used purely for
361   *                                   informational and/or troubleshooting
362   *                                   purposes.
363   * @param  commitDurability          The durability level that should be used
364   *                                   when committing the associated
365   *                                   transaction.  It may be {@code null} if
366   *                                   the server-default durability level
367   *                                   should be used.
368   * @param  backendLockBehavior       The behavior that should be used with
369   *                                   regard to acquiring an exclusive lock for
370   *                                   processing in the target backend.  It may
371   *                                   be {@code null} if the server-default
372   *                                   backend lock behavior should be used.
373   * @param  backendLockTimeoutMillis  The maximum length of time in
374   *                                   milliseconds to spend attempting to
375   *                                   acquire an exclusive backend lock if it
376   *                                   is needed during any part of the
377   *                                   processing.  A value that of zero
378   *                                   indicates that no timeout should be
379   *                                   enforced.  It may be {@code null} if the
380   *                                   server will determine the backend lock
381   *                                   timeout that should be used.
382   * @param  retryAttempts             The number of times to retry the
383   *                                   associated operations in a new
384   *                                   transaction if the initial attempt fails.
385   *                                   If this is {@code null}, then the server
386   *                                   will determine the number of retry
387   *                                   attempts to make.  Note that depending on
388   *                                   the backend lock behavior, the server may
389   *                                   make one additional retry attempt if
390   *                                   necessary after acquiring an exclusive
391   *                                   backend lock.
392   * @param  minTxnLockTimeoutMillis   The minimum database lock timeout that
393   *                                   should be used for the associated
394   *                                   transaction.  If this is specified, then
395   *                                   the first attempt will use this lock
396   *                                   timeout, and subsequent attempts will use
397   *                                   a timeout value between this and the
398   *                                   maximum database lock timeout (which must
399   *                                   also be specified).  If this is
400   *                                   {@code null}, then the server will
401   *                                   determine the database lock timeout
402   *                                   settings to use.
403   * @param  maxTxnLockTimeoutMillis   The maximum database lock timeout that
404   *                                   should be used for the associated
405   *                                   transaction.  If this is specified, then
406   *                                   the minimum database lock timeout must
407   *                                   also be specified, and this value must be
408   *                                   greater than or equal to the minimum lock
409   *                                   timeout.  If this is {@code null}, then
410   *                                   the server will determine the database
411   *                                   lock timeout settings to use.
412   * @param  returnResponseControl     Indicates whether to return a response
413   *                                   control with transaction-related
414   *                                   information collected over the course of
415   *                                   processing the associated operation.
416   */
417  public TransactionSettingsRequestControl(final boolean isCritical,
418       @Nullable final String transactionName,
419       @Nullable final TransactionSettingsCommitDurability commitDurability,
420       @Nullable final TransactionSettingsBackendLockBehavior
421            backendLockBehavior,
422       @Nullable final Long backendLockTimeoutMillis,
423       @Nullable final Integer retryAttempts,
424       @Nullable final Long minTxnLockTimeoutMillis,
425       @Nullable final Long maxTxnLockTimeoutMillis,
426       final boolean returnResponseControl)
427  {
428    super(TRANSACTION_SETTINGS_REQUEST_OID, isCritical,
429         encodeValue(transactionName, commitDurability, backendLockBehavior,
430              null, null, backendLockTimeoutMillis, retryAttempts,
431              minTxnLockTimeoutMillis, maxTxnLockTimeoutMillis, false,
432              returnResponseControl));
433
434    this.transactionName = transactionName;
435    this.commitDurability = commitDurability;
436    this.backendExclusiveLockBehavior = backendLockBehavior;
437    this.backendLockTimeoutMillis = backendLockTimeoutMillis;
438    this.minTxnLockTimeoutMillis = minTxnLockTimeoutMillis;
439    this.maxTxnLockTimeoutMillis = maxTxnLockTimeoutMillis;
440    this.retryAttempts = retryAttempts;
441    this.returnResponseControl = returnResponseControl;
442
443    singleWriterLockBehavior = null;
444    scopedLockDetails = null;
445    replicateControl = false;
446  }
447
448
449
450  /**
451   * Creates a new transaction settings request control with the provided
452   * information.
453   *
454   * @param  isCritical  Indicates whether the control should be considered
455   *                     critical.
456   * @param  properties  The properties to use for the request control.
457   */
458  public TransactionSettingsRequestControl(final boolean isCritical,
459       @NotNull final TransactionSettingsReqeustControlProperties properties)
460  {
461    super(TRANSACTION_SETTINGS_REQUEST_OID, isCritical,
462         encodeValue(properties.getTransactionName(),
463              properties.getCommitDurability(),
464              properties.getBackendExclusiveLockBehavior(),
465              properties.getSingleWriterLockBehavior(),
466              properties.getScopedLockDetails(),
467              properties.getBackendLockTimeoutMillis(),
468              properties.getRetryAttempts(),
469              properties.getMinTxnLockTimeoutMillis(),
470              properties.getMaxTxnLockTimeoutMillis(),
471              properties.replicateControl(),
472              properties.getReturnResponseControl()));
473
474    transactionName = properties.getTransactionName();
475    commitDurability = properties.getCommitDurability();
476    backendExclusiveLockBehavior = properties.getBackendExclusiveLockBehavior();
477    singleWriterLockBehavior = properties.getSingleWriterLockBehavior();
478    scopedLockDetails = properties.getScopedLockDetails();
479    backendLockTimeoutMillis = properties.getBackendLockTimeoutMillis();
480    minTxnLockTimeoutMillis = properties.getMinTxnLockTimeoutMillis();
481    maxTxnLockTimeoutMillis = properties.getMaxTxnLockTimeoutMillis();
482    retryAttempts = properties.getRetryAttempts();
483    replicateControl = properties.replicateControl();
484    returnResponseControl = properties.getReturnResponseControl();
485  }
486
487
488
489  /**
490   * Creates a new transaction settings request control that is decoded from the
491   * provided generic control.
492   *
493   * @param  c  The generic control to decode as a transaction settings request
494   *            control.
495   *
496   * @throws  LDAPException  If a problem is encountered while attempting to
497   *                         decode the provided control as a transaction
498   *                         settings request control.
499   */
500  public TransactionSettingsRequestControl(@NotNull final Control c)
501         throws LDAPException
502  {
503    super(c);
504
505    final ASN1OctetString value = c.getValue();
506    if (value == null)
507    {
508      throw new LDAPException(ResultCode.DECODING_ERROR,
509           ERR_TXN_SETTINGS_REQUEST_MISSING_VALUE.get());
510    }
511
512    try
513    {
514      boolean replicate = false;
515      boolean responseControl = false;
516      Integer numRetries = null;
517      Long backendTimeout = null;
518      Long maxTxnLockTimeout = null;
519      Long minTxnLockTimeout = null;
520      String txnName = null;
521      TransactionSettingsBackendLockBehavior exclusiveLockBehavior = null;
522      TransactionSettingsBackendLockBehavior swLockBehavior = null;
523      TransactionSettingsCommitDurability durability = null;
524      TransactionSettingsScopedLockDetails scopeDetails = null;
525
526      for (final ASN1Element e :
527           ASN1Sequence.decodeAsSequence(value.getValue()).elements())
528      {
529        switch (e.getType())
530        {
531          case TYPE_TXN_NAME:
532            txnName = ASN1OctetString.decodeAsOctetString(e).stringValue();
533            break;
534
535          case TYPE_COMMIT_DURABILITY:
536            durability = TransactionSettingsCommitDurability.valueOf(
537                 ASN1Enumerated.decodeAsEnumerated(e).intValue());
538            if (durability == null)
539            {
540              throw new LDAPException(ResultCode.DECODING_ERROR,
541                   ERR_TXN_SETTINGS_REQUEST_UNKNOWN_DURABILITY.get(
542                        ASN1Enumerated.decodeAsEnumerated(e).intValue()));
543            }
544            break;
545
546          case TYPE_BACKEND_EXCLUSIVE_LOCK_BEHAVIOR:
547            exclusiveLockBehavior =
548                 TransactionSettingsBackendLockBehavior.valueOf(
549                      ASN1Enumerated.decodeAsEnumerated(e).intValue());
550            if (exclusiveLockBehavior == null)
551            {
552              throw new LDAPException(ResultCode.DECODING_ERROR,
553                   ERR_TXN_SETTINGS_REQUEST_UNKNOWN_EXCLUSIVE_LOCK_BEHAVIOR.get(
554                        ASN1Enumerated.decodeAsEnumerated(e).intValue()));
555            }
556            break;
557
558          case TYPE_BACKEND_LOCK_TIMEOUT:
559            backendTimeout = ASN1Long.decodeAsLong(e).longValue();
560            if (backendTimeout < 0L)
561            {
562              throw new LDAPException(ResultCode.DECODING_ERROR,
563                   ERR_TXN_SETTINGS_REQUEST_INVALID_BACKEND_LOCK_TIMEOUT.get(
564                        backendTimeout));
565            }
566            break;
567
568          case TYPE_RETRY_ATTEMPTS:
569            numRetries = ASN1Integer.decodeAsInteger(e).intValue();
570            if (numRetries < 0)
571            {
572              throw new LDAPException(ResultCode.DECODING_ERROR,
573                   ERR_TXN_SETTINGS_REQUEST_INVALID_RETRY_ATTEMPTS.get(
574                        numRetries));
575            }
576            break;
577
578          case TYPE_TXN_LOCK_TIMEOUT:
579            final ASN1Element[] timeoutElements =
580                 ASN1Sequence.decodeAsSequence(e).elements();
581            minTxnLockTimeout =
582                 ASN1Long.decodeAsLong(timeoutElements[0]).longValue();
583            maxTxnLockTimeout =
584                 ASN1Long.decodeAsLong(timeoutElements[1]).longValue();
585            if (minTxnLockTimeout < 0)
586            {
587              throw new LDAPException(ResultCode.DECODING_ERROR,
588                   ERR_TXN_SETTINGS_REQUEST_INVALID_MIN_TXN_LOCK_TIMEOUT.get(
589                        minTxnLockTimeout));
590            }
591            if (maxTxnLockTimeout < minTxnLockTimeout)
592            {
593              throw new LDAPException(ResultCode.DECODING_ERROR,
594                   ERR_TXN_SETTINGS_REQUEST_INVALID_MAX_TXN_LOCK_TIMEOUT.get(
595                        maxTxnLockTimeout, minTxnLockTimeout));
596            }
597            break;
598
599          case TYPE_SINGLE_WRITER_LOCK_BEHAVIOR:
600            swLockBehavior =
601                 TransactionSettingsBackendLockBehavior.valueOf(
602                      ASN1Enumerated.decodeAsEnumerated(e).intValue());
603            if (swLockBehavior == null)
604            {
605              throw new LDAPException(ResultCode.DECODING_ERROR,
606                   ERR_TXN_SETTINGS_REQUEST_UNKNOWN_SW_LOCK_BEHAVIOR.get(
607                        ASN1Enumerated.decodeAsEnumerated(e).intValue()));
608            }
609            break;
610
611          case TYPE_SCOPED_LOCK_DETAILS:
612            scopeDetails = TransactionSettingsScopedLockDetails.decode(e);
613            break;
614
615          case TYPE_REPLICATE_CONTROL:
616            replicate = ASN1Boolean.decodeAsBoolean(e).booleanValue();
617            break;
618
619          case TYPE_RETURN_RESPONSE_CONTROL:
620            responseControl = ASN1Boolean.decodeAsBoolean(e).booleanValue();
621            break;
622
623          default:
624            throw new LDAPException(ResultCode.DECODING_ERROR,
625                 ERR_TXN_SETTINGS_REQUEST_UNRECOGNIZED_ELEMENT_TYPE.get(
626                      StaticUtils.toHex(e.getType())));
627        }
628      }
629
630      transactionName = txnName;
631      commitDurability = durability;
632      backendExclusiveLockBehavior = exclusiveLockBehavior;
633      singleWriterLockBehavior = swLockBehavior;
634      scopedLockDetails = scopeDetails;
635      backendLockTimeoutMillis = backendTimeout;
636      minTxnLockTimeoutMillis = minTxnLockTimeout;
637      maxTxnLockTimeoutMillis = maxTxnLockTimeout;
638      retryAttempts = numRetries;
639      replicateControl = replicate;
640      returnResponseControl = responseControl;
641    }
642    catch (final LDAPException le)
643    {
644      Debug.debugException(le);
645      throw le;
646    }
647    catch (final Exception e)
648    {
649      Debug.debugException(e);
650      throw new LDAPException(ResultCode.DECODING_ERROR,
651           ERR_TXN_SETTINGS_REQUEST_ERROR_DECODING_VALUE.get(
652                StaticUtils.getExceptionMessage(e)),
653           e);
654    }
655  }
656
657
658
659  /**
660   * Encodes the provided information into a form suitable for use as the value
661   * of this ASN.1 element.
662   *
663   * @param  transactionName           The name to use for the transaction.  It
664   *                                   may be {@code null} if no
665   *                                   client-specified transaction name is
666   *                                   needed.  If a transaction name is
667   *                                   provided, it will be used purely for
668   *                                   informational and/or troubleshooting
669   *                                   purposes.
670   * @param  commitDurability          The durability level that should be used
671   *                                   when committing the associated
672   *                                   transaction.  It may be {@code null} if
673   *                                   the server-default durability level
674   *                                   should be used.
675   * @param  backendLockBehavior       The behavior that should be used with
676   *                                   regard to acquiring an exclusive lock for
677   *                                   processing in the target backend.  It may
678   *                                   be {@code null} if the server-default
679   *                                   backend exclusive lock behavior should be
680   *                                   used.
681   * @param  singleWriterLockBehavior  The behavior that should be used with
682   *                                   regard to acquiring a single-writer lock
683   *                                   for processing in the target backend.  It
684   *                                   may be {@code null} if the server-default
685   *                                   single-writer lock behavior should be
686   *                                   used.
687   * @param  scopedLockDetails         Details about the conditions under which
688   *                                   the server should attempt to acquire a
689   *                                   scoped lock.  It may be {@code null} if
690   *                                   no attempt should be made to acquire a
691   *                                   scoped lock.
692   * @param  backendLockTimeoutMillis  The maximum length of time in
693   *                                   milliseconds to spend attempting to
694   *                                   acquire an exclusive backend lock if it
695   *                                   is needed during any part of the
696   *                                   processing.  A value that of zero
697   *                                   indicates that no timeout should be
698   *                                   enforced.  It may be {@code null} if the
699   *                                   server will determine the backend lock
700   *                                   timeout that should be used.
701   * @param  retryAttempts             The number of times to retry the
702   *                                   associated operations in a new
703   *                                   transaction if the initial attempt fails.
704   *                                   If this is {@code null}, then the server
705   *                                   will determine the number of retry
706   *                                   attempts to make.  Note that depending on
707   *                                   the backend lock behavior, the server may
708   *                                   make one additional retry attempt if
709   *                                   necessary after acquiring an exclusive
710   *                                   backend lock.
711   * @param  minTxnLockTimeoutMillis   The minimum database lock timeout that
712   *                                   should be used for the associated
713   *                                   transaction.  If this is specified, then
714   *                                   the first attempt will use this lock
715   *                                   timeout, and subsequent attempts will use
716   *                                   a timeout value between this and the
717   *                                   maximum database lock timeout (which must
718   *                                   also be specified).  If this is
719   *                                   {@code null}, then the server will
720   *                                   determine the database lock timeout
721   *                                   settings to use.
722   * @param  maxTxnLockTimeoutMillis   The maximum database lock timeout that
723   *                                   should be used for the associated
724   *                                   transaction.  If this is specified, then
725   *                                   the minimum database lock timeout must
726   *                                   also be specified, and this value must be
727   *                                   greater than or equal to the minimum lock
728   *                                   timeout.  If this is {@code null}, then
729   *                                   the server will determine the database
730   *                                   lock timeout settings to use.
731   * @param  replicateControl          Indicates whether the settings in this
732   *                                   control should be considered when the
733   *                                   operation is replicated to other servers
734   *                                   in the topology.
735   * @param  returnResponseControl     Indicates whether to return a response
736   *                                   control with transaction-related
737   *                                   information collected over the course of
738   *                                   processing the associated operation.
739   *
740   * @return  The encoded value to use for the control.
741   */
742  @NotNull()
743  private static ASN1OctetString encodeValue(
744       @Nullable final String transactionName,
745       @Nullable final TransactionSettingsCommitDurability commitDurability,
746       @Nullable final TransactionSettingsBackendLockBehavior
747            backendLockBehavior,
748       @Nullable final TransactionSettingsBackendLockBehavior
749            singleWriterLockBehavior,
750       @Nullable final TransactionSettingsScopedLockDetails scopedLockDetails,
751       @Nullable final Long backendLockTimeoutMillis,
752       @Nullable final Integer retryAttempts,
753       @Nullable final Long minTxnLockTimeoutMillis,
754       @Nullable final Long maxTxnLockTimeoutMillis,
755       final boolean replicateControl,
756       final boolean returnResponseControl)
757  {
758    final ArrayList<ASN1Element> elements = new ArrayList<>(10);
759
760    if (transactionName != null)
761    {
762      elements.add(new ASN1OctetString(TYPE_TXN_NAME, transactionName));
763    }
764
765    if (commitDurability != null)
766    {
767      elements.add(new ASN1Enumerated(TYPE_COMMIT_DURABILITY,
768           commitDurability.intValue()));
769    }
770
771    if (backendLockBehavior != null)
772    {
773      elements.add(new ASN1Enumerated(TYPE_BACKEND_EXCLUSIVE_LOCK_BEHAVIOR,
774           backendLockBehavior.intValue()));
775    }
776
777    if (backendLockTimeoutMillis != null)
778    {
779      Validator.ensureTrue((backendLockTimeoutMillis >= 0L),
780           "If a backend lock timeout is specified, then it must be greater " +
781                "than or equal to zero.");
782      elements.add(new ASN1Long(TYPE_BACKEND_LOCK_TIMEOUT,
783           backendLockTimeoutMillis));
784    }
785
786    if (retryAttempts != null)
787    {
788      Validator.ensureTrue((retryAttempts >= 0),
789           "If specified, the number of retry attempts must be greater than " +
790                "or equal to zero.");
791
792      elements.add(new ASN1Integer(TYPE_RETRY_ATTEMPTS, retryAttempts));
793    }
794
795    if (minTxnLockTimeoutMillis != null)
796    {
797      Validator.ensureTrue((maxTxnLockTimeoutMillis != null),
798           "If a minimum transaction lock timeout is specified, then a " +
799                "maximum transaction lock timeout must also be specified.");
800      Validator.ensureTrue((minTxnLockTimeoutMillis > 0),
801           "If a minimum transaction lock timeout is specified, then it must " +
802                "be greater than zero.");
803      Validator.ensureTrue((maxTxnLockTimeoutMillis >= minTxnLockTimeoutMillis),
804           "If a minimum transaction lock timeout is specified, then it must " +
805                "be less than or equal to the minimum transaction lock " +
806                "timeout.");
807      elements.add(new ASN1Sequence(TYPE_TXN_LOCK_TIMEOUT,
808           new ASN1Long(minTxnLockTimeoutMillis),
809           new ASN1Long(maxTxnLockTimeoutMillis)));
810    }
811    else
812    {
813      Validator.ensureTrue((maxTxnLockTimeoutMillis == null),
814           "If a maximum transaction lock timeout is specified, then a " +
815                "minimum transaction lock timeout must also be specified.");
816    }
817
818    if (returnResponseControl)
819    {
820      elements.add(new ASN1Boolean(TYPE_RETURN_RESPONSE_CONTROL, true));
821    }
822
823    if (singleWriterLockBehavior != null)
824    {
825      elements.add(new ASN1Enumerated(TYPE_SINGLE_WRITER_LOCK_BEHAVIOR,
826           singleWriterLockBehavior.intValue()));
827    }
828
829    if (scopedLockDetails != null)
830    {
831      final ASN1Element scopedLockDetailsElement =
832           scopedLockDetails.encode();
833      if (scopedLockDetailsElement != null)
834      {
835        elements.add(scopedLockDetailsElement);
836      }
837    }
838
839    if (replicateControl)
840    {
841      elements.add(new ASN1Boolean(TYPE_REPLICATE_CONTROL, true));
842    }
843
844    return new ASN1OctetString(new ASN1Sequence(elements).encode());
845  }
846
847
848
849  /**
850   * Retrieves the name to assign to the associated transaction, if specified.
851   *
852   * @return  The name to assign to the associated transaction, or {@code null}
853   *          if none has been specified.
854   */
855  @Nullable()
856  public String getTransactionName()
857  {
858    return transactionName;
859  }
860
861
862
863  /**
864   * Retrieves the commit durability that should be used for the associated
865   * transaction, if specified.
866   *
867   * @return  The commit durability that should be used for the associated
868   *          transaction, or {@code null} if none has been specified and the
869   *          server should determine the commit durability.
870   */
871  @Nullable()
872  public TransactionSettingsCommitDurability getCommitDurability()
873  {
874    return commitDurability;
875  }
876
877
878
879  /**
880   * Retrieves the backend exclusive lock behavior that should be used for the
881   * associated transaction, if specified.
882   *
883   * @return  The backend exclusive lock behavior that should be used for the
884   *          associated transaction, or {@code null} if none has been specified
885   *          and the server should determine the backend exclusive lock
886   *          behavior.
887   */
888  @Nullable()
889  public TransactionSettingsBackendLockBehavior getBackendLockBehavior()
890  {
891    return backendExclusiveLockBehavior;
892  }
893
894
895
896  /**
897   * Retrieves the single-wwriter lock behavior that should be used for the
898   * associated transaction, if specified.
899   *
900   * @return  The single-writer lock behavior that should be used for the
901   *          associated transaction, or {@code null} if none has been specified
902   *          and the server should determine the backend exclusive lock
903   *          behavior.
904   */
905  @Nullable()
906  public TransactionSettingsBackendLockBehavior getSingleWriterLockBehavior()
907  {
908    return singleWriterLockBehavior;
909  }
910
911
912
913  /**
914   * Retrieves details about the conditions under which the server should
915   * attempt to acquire a scoped lock.
916   *
917   * @return  Details about the conditions under which the server should attempt
918   *          to acquire a scoped lock, or {@code null} if no attempt should be
919   *          made to acquire a scoped lock.
920   */
921  @Nullable()
922  public TransactionSettingsScopedLockDetails getScopedLockDetails()
923  {
924    return scopedLockDetails;
925  }
926
927
928
929  /**
930   * Retrieves the backend lock timeout (in milliseconds) that should be used
931   * for the associated transaction, if specified.
932   *
933   * @return  The backend lock timeout (in milliseconds) that should be used for
934   *          the associated transaction, or {@code null} if none has been
935   *          specified and the server should determine the backend lock
936   *          timeout.
937   */
938  @Nullable()
939  public Long getBackendLockTimeoutMillis()
940  {
941    return backendLockTimeoutMillis;
942  }
943
944
945
946  /**
947   * Retrieves the maximum number of times that the transaction may be retried
948   * if the initial attempt fails due to a lock conflict, if specified.
949   *
950   * @return  The maximum number of times that the transaction may be retried if
951   *          the initial attempt fails due to a lock conflict, or {@code null}
952   *          if none has been specified and the server should determine the
953   *          number of retry attempts.
954   */
955  @Nullable()
956  public Integer getRetryAttempts()
957  {
958    return retryAttempts;
959  }
960
961
962
963  /**
964   * Retrieves the minimum transaction lock timeout (in milliseconds) that
965   * should be used for the associated transaction, if specified.  This is the
966   * timeout value that will be used for the first attempt.  Any subsequent
967   * attempts will have a lock timeout that is between the minimum and maximum
968   * timeout value.
969   *
970   * @return  The minimum lock timeout (in milliseconds) that should
971   *          be used for the associated transaction, or {@code null} if none
972   *          has been specified and the server should determine the minimum
973   *          transaction lock timeout.
974   */
975  @Nullable()
976  public Long getMinTxnLockTimeoutMillis()
977  {
978    return minTxnLockTimeoutMillis;
979  }
980
981
982
983  /**
984   * Retrieves the maximum transaction lock timeout (in milliseconds) that
985   * should be used for the associated transaction, if specified.  The timeout
986   * to be used for any retries will be between the minimum and maximum lock
987   * timeout values.
988   *
989   * @return  The maximum lock timeout (in milliseconds) that should
990   *          be used for the associated transaction, or {@code null} if none
991   *          has been specified and the server should determine the maximum
992   *          transaction lock timeout.
993   */
994  @Nullable()
995  public Long getMaxTxnLockTimeoutMillis()
996  {
997    return maxTxnLockTimeoutMillis;
998  }
999
1000
1001
1002  /**
1003   * Indicates whether the settings in this control should be considered when
1004   * the operation is replicated to other servers in the topology.
1005   *
1006   * @return  {@code true} if the control settings should be replicated, or
1007   *          {@code false} if not.
1008   */
1009  public boolean replicateControl()
1010  {
1011    return replicateControl;
1012  }
1013
1014
1015
1016  /**
1017   * Indicates whether to return a response control with transaction-related
1018   * information collected over the course of processing the associated
1019   * operation.
1020   *
1021   * @return  {@code true} if the server should return a response control with
1022   *          transaction-related information, or {@code false} if not.
1023   */
1024  public boolean returnResponseControl()
1025  {
1026    return returnResponseControl;
1027  }
1028
1029
1030
1031  /**
1032   * {@inheritDoc}
1033   */
1034  @Override()
1035  @NotNull()
1036  public String getControlName()
1037  {
1038    return INFO_CONTROL_NAME_TXN_SETTINGS_REQUEST.get();
1039  }
1040
1041
1042
1043  /**
1044   * {@inheritDoc}
1045   */
1046  @Override()
1047  public void toString(@NotNull final StringBuilder buffer)
1048  {
1049    buffer.append("TransactionSettingsRequestControl(isCritical=");
1050    buffer.append(isCritical());
1051
1052    if (transactionName != null)
1053    {
1054      buffer.append(", transactionName='");
1055      buffer.append(transactionName);
1056      buffer.append('\'');
1057    }
1058
1059    if (commitDurability != null)
1060    {
1061      buffer.append(", commitDurability='");
1062      buffer.append(commitDurability.name());
1063      buffer.append('\'');
1064    }
1065
1066    if (backendExclusiveLockBehavior != null)
1067    {
1068      buffer.append(", backendExclusiveLockBehavior='");
1069      buffer.append(backendExclusiveLockBehavior.name());
1070      buffer.append('\'');
1071    }
1072
1073    if (singleWriterLockBehavior != null)
1074    {
1075      buffer.append(", singleWriterLockBehavior='");
1076      buffer.append(singleWriterLockBehavior.name());
1077      buffer.append('\'');
1078    }
1079
1080    if (scopedLockDetails != null)
1081    {
1082      buffer.append(", scopedLockDetails=");
1083      scopedLockDetails.toString(buffer);
1084    }
1085
1086    if (backendLockTimeoutMillis != null)
1087    {
1088      buffer.append(", backendLockTimeoutMillis=");
1089      buffer.append(backendLockTimeoutMillis);
1090    }
1091
1092    if (retryAttempts != null)
1093    {
1094      buffer.append(", retryAttempts=");
1095      buffer.append(retryAttempts);
1096    }
1097
1098    if (minTxnLockTimeoutMillis != null)
1099    {
1100      buffer.append(", minTxnLockTimeoutMillis=");
1101      buffer.append(minTxnLockTimeoutMillis);
1102    }
1103
1104    if (maxTxnLockTimeoutMillis != null)
1105    {
1106      buffer.append(", maxTxnLockTimeoutMillis=");
1107      buffer.append(maxTxnLockTimeoutMillis);
1108    }
1109
1110    buffer.append(", replicateControl=");
1111    buffer.append(replicateControl);
1112
1113    buffer.append(", returnResponseControl=");
1114    buffer.append(returnResponseControl);
1115
1116    buffer.append(')');
1117  }
1118}