001/*
002 * Copyright 2010-2014 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2010-2014 UnboundID Corp.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021package com.unboundid.ldap.sdk.extensions;
022
023
024
025import com.unboundid.asn1.ASN1Boolean;
026import com.unboundid.asn1.ASN1Element;
027import com.unboundid.asn1.ASN1OctetString;
028import com.unboundid.asn1.ASN1Sequence;
029import com.unboundid.ldap.sdk.Control;
030import com.unboundid.ldap.sdk.ExtendedRequest;
031import com.unboundid.ldap.sdk.ExtendedResult;
032import com.unboundid.ldap.sdk.LDAPConnection;
033import com.unboundid.ldap.sdk.LDAPException;
034import com.unboundid.ldap.sdk.ResultCode;
035import com.unboundid.util.NotMutable;
036import com.unboundid.util.ThreadSafety;
037import com.unboundid.util.ThreadSafetyLevel;
038
039import static com.unboundid.ldap.sdk.extensions.ExtOpMessages.*;
040import static com.unboundid.util.Debug.*;
041import static com.unboundid.util.Validator.*;
042
043
044
045/**
046 * This class provides an implementation of the end transaction extended
047 * request as defined in
048 * <A HREF="http://www.ietf.org/rfc/rfc5805.txt">RFC 5805</A>.  It may be used
049 * to either commit or abort a transaction that was created using the start
050 * transaction request.  See the documentation for the
051 * {@link StartTransactionExtendedRequest} class for an example of processing an
052 * LDAP transaction.
053 */
054@NotMutable()
055@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
056public final class EndTransactionExtendedRequest
057       extends ExtendedRequest
058{
059  /**
060   * The OID (1.3.6.1.1.21.3) for the end transaction extended request.
061   */
062  public static final String END_TRANSACTION_REQUEST_OID = "1.3.6.1.1.21.3";
063
064
065
066  /**
067   * The serial version UID for this serializable class.
068   */
069  private static final long serialVersionUID = -7135468264026410702L;
070
071
072
073  // The transaction ID for the associated transaction.
074  private final ASN1OctetString transactionID;
075
076  // Indicates whether to commit or abort the associated transaction.
077  private final boolean commit;
078
079
080
081  /**
082   * Creates a new end transaction extended request with the provided
083   * information.
084   *
085   * @param  transactionID  The transaction ID for the transaction to commit or
086   *                        abort.  It must not be {@code null}.
087   * @param  commit         {@code true} if the transaction should be committed,
088   *                        or {@code false} if the transaction should be
089   *                        aborted.
090   * @param  controls       The set of controls to include in the request.
091   */
092  public EndTransactionExtendedRequest(final ASN1OctetString transactionID,
093                                       final boolean commit,
094                                       final Control... controls)
095  {
096    super(END_TRANSACTION_REQUEST_OID, encodeValue(transactionID, commit),
097          controls);
098
099    this.transactionID = transactionID;
100    this.commit        = commit;
101  }
102
103
104
105  /**
106   * Creates a new end transaction extended request from the provided generic
107   * extended request.
108   *
109   * @param  extendedRequest  The generic extended request to use to create this
110   *                          end transaction extended request.
111   *
112   * @throws  LDAPException  If a problem occurs while decoding the request.
113   */
114  public EndTransactionExtendedRequest(final ExtendedRequest extendedRequest)
115         throws LDAPException
116  {
117    super(extendedRequest);
118
119    final ASN1OctetString value = extendedRequest.getValue();
120    if (value == null)
121    {
122      throw new LDAPException(ResultCode.DECODING_ERROR,
123           ERR_END_TXN_REQUEST_NO_VALUE.get());
124    }
125
126    try
127    {
128      final ASN1Element valueElement = ASN1Element.decode(value.getValue());
129      final ASN1Element[] elements =
130           ASN1Sequence.decodeAsSequence(valueElement).elements();
131      if (elements.length == 1)
132      {
133        commit        = true;
134        transactionID = ASN1OctetString.decodeAsOctetString(elements[0]);
135      }
136      else
137      {
138        commit        = ASN1Boolean.decodeAsBoolean(elements[0]).booleanValue();
139        transactionID = ASN1OctetString.decodeAsOctetString(elements[1]);
140      }
141    }
142    catch (Exception e)
143    {
144      debugException(e);
145      throw new LDAPException(ResultCode.DECODING_ERROR,
146           ERR_END_TXN_REQUEST_CANNOT_DECODE.get(e), e);
147    }
148  }
149
150
151
152  /**
153   * Generates the value to include in this extended request.
154   *
155   * @param  transactionID  The transaction ID for the transaction to commit or
156   *                        abort.  It must not be {@code null}.
157   * @param  commit         {@code true} if the transaction should be committed,
158   *                        or {@code false} if the transaction should be
159   *                        aborted.
160   *
161   * @return  The ASN.1 octet string containing the encoded request value.
162   */
163  private static ASN1OctetString
164       encodeValue(final ASN1OctetString transactionID,
165                   final boolean commit)
166  {
167    ensureNotNull(transactionID);
168
169    final ASN1Element[] valueElements;
170    if (commit)
171    {
172      valueElements = new ASN1Element[]
173      {
174        transactionID
175      };
176    }
177    else
178    {
179      valueElements = new ASN1Element[]
180      {
181        new ASN1Boolean(commit),
182        transactionID
183      };
184    }
185
186    return new ASN1OctetString(new ASN1Sequence(valueElements).encode());
187  }
188
189
190
191  /**
192   * Retrieves the transaction ID for the transaction to commit or abort.
193   *
194   * @return  The transaction ID for the transaction to commit or abort.
195   */
196  public ASN1OctetString getTransactionID()
197  {
198    return transactionID;
199  }
200
201
202
203  /**
204   * Indicates whether the transaction should be committed or aborted.
205   *
206   * @return  {@code true} if the transaction should be committed, or
207   *          {@code false} if it should be aborted.
208   */
209  public boolean commit()
210  {
211    return commit;
212  }
213
214
215
216  /**
217   * {@inheritDoc}
218   */
219  @Override()
220  public EndTransactionExtendedResult process(final LDAPConnection connection,
221                                              final int depth)
222         throws LDAPException
223  {
224    final ExtendedResult extendedResponse = super.process(connection, depth);
225    return new EndTransactionExtendedResult(extendedResponse);
226  }
227
228
229
230  /**
231   * {@inheritDoc}
232   */
233  @Override()
234  public EndTransactionExtendedRequest duplicate()
235  {
236    return duplicate(getControls());
237  }
238
239
240
241  /**
242   * {@inheritDoc}
243   */
244  @Override()
245  public EndTransactionExtendedRequest duplicate(final Control[] controls)
246  {
247    final EndTransactionExtendedRequest r =
248         new EndTransactionExtendedRequest(transactionID, commit, controls);
249    r.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
250    return r;
251  }
252
253
254
255  /**
256   * {@inheritDoc}
257   */
258  @Override()
259  public String getExtendedRequestName()
260  {
261    return INFO_EXTENDED_REQUEST_NAME_END_TXN.get();
262  }
263
264
265
266  /**
267   * {@inheritDoc}
268   */
269  @Override()
270  public void toString(final StringBuilder buffer)
271  {
272    buffer.append("EndTransactionExtendedRequest(transactionID='");
273    buffer.append(transactionID.stringValue());
274    buffer.append("', commit=");
275    buffer.append(commit);
276
277    final Control[] controls = getControls();
278    if (controls.length > 0)
279    {
280      buffer.append("controls={");
281      for (int i=0; i < controls.length; i++)
282      {
283        if (i > 0)
284        {
285          buffer.append(", ");
286        }
287
288        buffer.append(controls[i]);
289      }
290      buffer.append('}');
291    }
292
293    buffer.append(')');
294  }
295}