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