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 java.util.ArrayList;
026    import java.util.Collections;
027    import java.util.List;
028    
029    import com.unboundid.asn1.ASN1Element;
030    import com.unboundid.asn1.ASN1OctetString;
031    import com.unboundid.asn1.ASN1Sequence;
032    import com.unboundid.ldap.sdk.Control;
033    import com.unboundid.ldap.sdk.ExtendedResult;
034    import com.unboundid.ldap.sdk.LDAPException;
035    import com.unboundid.ldap.sdk.ResultCode;
036    import com.unboundid.util.NotMutable;
037    import com.unboundid.util.ThreadSafety;
038    import com.unboundid.util.ThreadSafetyLevel;
039    
040    import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*;
041    import static com.unboundid.util.Debug.*;
042    import static com.unboundid.util.StaticUtils.*;
043    
044    
045    
046    /**
047     * <BLOCKQUOTE>
048     *   <B>NOTE:</B>  This class is part of the Commercial Edition of the UnboundID
049     *   LDAP SDK for Java.  It is not available for use in applications that
050     *   include only the Standard Edition of the LDAP SDK, and is not supported for
051     *   use in conjunction with non-UnboundID products.
052     * </BLOCKQUOTE>
053     * This class implements a data structure for storing the information from an
054     * extended result for the start interactive transaction extended request.  It
055     * is able to decode a generic extended result to extract the transaction ID and
056     * base DNs that it may contain, if the operation was successful.
057     * <BR><BR>
058     * See the documentation for the
059     * {@link StartInteractiveTransactionExtendedRequest} class for an example that
060     * demonstrates the use of interactive transactions.
061     */
062    @NotMutable()
063    @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
064    public final class StartInteractiveTransactionExtendedResult
065           extends ExtendedResult
066    {
067      /**
068       * The BER type for the {@code txnID} element of the response.
069       */
070      private static final byte TYPE_TXN_ID = (byte) 0x80;
071    
072    
073    
074      /**
075       * The BER type for the {@code baseDNs} element of the response.
076       */
077      private static final byte TYPE_BASE_DNS = (byte) 0xA1;
078    
079    
080    
081      /**
082       * The serial version UID for this serializable class.
083       */
084      private static final long serialVersionUID = 4010094216900393866L;
085    
086    
087    
088      // The transaction ID returned by the server.
089      private final ASN1OctetString transactionID;
090    
091      // The list of base DNs returned by the server, if any.
092      private final List<String> baseDNs;
093    
094    
095    
096      /**
097       * Creates a new start interactive transaction extended result from the
098       * provided extended result.
099       *
100       * @param  extendedResult  The extended result to be decoded as a start
101       *                         interactive transaction extended result.  It must
102       *                         not be {@code null}.
103       *
104       * @throws  LDAPException  If a problem occurs while attempting to decode the
105       *                         provided extended result as a start interactive
106       *                         transaction extended result.
107       */
108      public StartInteractiveTransactionExtendedResult(
109                  final ExtendedResult extendedResult)
110             throws LDAPException
111      {
112        super(extendedResult);
113    
114        if (! extendedResult.hasValue())
115        {
116          transactionID = null;
117          baseDNs       = null;
118          return;
119        }
120    
121        final ASN1Sequence valueSequence;
122        try
123        {
124          final ASN1Element valueElement =
125               ASN1Element.decode(extendedResult.getValue().getValue());
126          valueSequence = ASN1Sequence.decodeAsSequence(valueElement);
127        }
128        catch (Exception e)
129        {
130          debugException(e);
131          throw new LDAPException(ResultCode.DECODING_ERROR,
132               ERR_START_INT_TXN_RESULT_VALUE_NOT_SEQUENCE.get(e.getMessage()), e);
133        }
134    
135        ASN1OctetString txnID      = null;
136        List<String>    baseDNList = null;
137        for (final ASN1Element element : valueSequence.elements())
138        {
139          switch (element.getType())
140          {
141            case TYPE_TXN_ID:
142              txnID = ASN1OctetString.decodeAsOctetString(element);
143              break;
144            case TYPE_BASE_DNS:
145              try
146              {
147                final ASN1Sequence baseDNsSequence =
148                     ASN1Sequence.decodeAsSequence(element);
149                final ArrayList<String> dnList =
150                     new ArrayList<String>(baseDNsSequence.elements().length);
151                for (final ASN1Element e : baseDNsSequence.elements())
152                {
153                  dnList.add(ASN1OctetString.decodeAsOctetString(e).stringValue());
154                }
155                baseDNList = Collections.unmodifiableList(dnList);
156              }
157              catch (Exception e)
158              {
159                debugException(e);
160                throw new LDAPException(ResultCode.DECODING_ERROR,
161                     ERR_START_INT_TXN_RESULT_BASE_DNS_NOT_SEQUENCE.get(
162                          e.getMessage()), e);
163              }
164              break;
165            default:
166              throw new LDAPException(ResultCode.DECODING_ERROR,
167                   ERR_START_INT_TXN_RESULT_INVALID_ELEMENT.get(
168                        toHex(element.getType())));
169          }
170        }
171    
172        transactionID = txnID;
173        baseDNs       =  baseDNList;
174    
175        if (transactionID == null)
176        {
177          throw new LDAPException(ResultCode.DECODING_ERROR,
178                                  ERR_START_INT_TXN_RESULT_NO_TXN_ID.get());
179        }
180      }
181    
182    
183    
184      /**
185       * Creates a new start interactive transaction extended result with the
186       * provided information.
187       *
188       * @param  messageID          The message ID for the LDAP message that is
189       *                            associated with this LDAP result.
190       * @param  resultCode         The result code from the response.
191       * @param  diagnosticMessage  The diagnostic message from the response, if
192       *                            available.
193       * @param  matchedDN          The matched DN from the response, if available.
194       * @param  referralURLs       The set of referral URLs from the response, if
195       *                            available.
196       * @param  transactionID      The transaction ID for this response, if
197       *                            available.
198       * @param  baseDNs            The list of base DNs for this response, if
199       *                            available.
200       * @param  responseControls   The set of controls from the response, if
201       *                            available.
202       */
203      public StartInteractiveTransactionExtendedResult(final int messageID,
204                  final ResultCode resultCode, final String diagnosticMessage,
205                  final String matchedDN, final String[] referralURLs,
206                  final ASN1OctetString transactionID, final List<String> baseDNs,
207                  final Control[] responseControls)
208      {
209        super(messageID, resultCode, diagnosticMessage, matchedDN, referralURLs,
210              null, encodeValue(transactionID, baseDNs), responseControls);
211    
212        this.transactionID = transactionID;
213    
214        if (baseDNs == null)
215        {
216          this.baseDNs = null;
217        }
218        else
219        {
220          this.baseDNs =
221               Collections.unmodifiableList(new ArrayList<String>(baseDNs));
222        }
223      }
224    
225    
226    
227      /**
228       * Encodes the provided information into an ASN.1 octet string suitable for
229       * use as the value of this extended result.
230       *
231       * @param  transactionID  The transaction ID for this response, if available.
232       * @param  baseDNs        The list of base DNs for this response, if
233       *                        available.
234       *
235       * @return  The ASN.1 octet string containing the encoded value, or
236       *          {@code null} if no value should be used.
237       */
238      private static ASN1OctetString encodeValue(
239                                          final ASN1OctetString transactionID,
240                                          final List<String> baseDNs)
241      {
242        if ((transactionID == null) && (baseDNs == null))
243        {
244          return null;
245        }
246    
247        final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(2);
248        if (transactionID != null)
249        {
250          elements.add(new ASN1OctetString(TYPE_TXN_ID, transactionID.getValue()));
251        }
252    
253        if ((baseDNs != null) && (! baseDNs.isEmpty()))
254        {
255          final ArrayList<ASN1Element> baseDNElements =
256               new ArrayList<ASN1Element>(baseDNs.size());
257          for (final String s : baseDNs)
258          {
259            baseDNElements.add(new ASN1OctetString(s));
260          }
261          elements.add(new ASN1Sequence(TYPE_BASE_DNS, baseDNElements));
262        }
263    
264        return new ASN1OctetString(new ASN1Sequence(elements).encode());
265      }
266    
267    
268    
269      /**
270       * Retrieves the transaction ID for this start interactive transaction
271       * extended result, if available.
272       *
273       * @return  The transaction ID for this start interactive transaction extended
274       *          result, or {@code null} if none was provided.
275       */
276      public ASN1OctetString getTransactionID()
277      {
278        return transactionID;
279      }
280    
281    
282    
283      /**
284       * Retrieves the list of base DNs for this start interactive transaction
285       * extended result, if available.
286       *
287       * @return  The list of base DNs for this start interactive transaction
288       *          extended result, or {@code null} if no base DN list was provided.
289       */
290      public List<String> getBaseDNs()
291      {
292        return baseDNs;
293      }
294    
295    
296    
297      /**
298       * {@inheritDoc}
299       */
300      @Override()
301      public String getExtendedResultName()
302      {
303        return INFO_EXTENDED_RESULT_NAME_START_INTERACTIVE_TXN.get();
304      }
305    
306    
307    
308      /**
309       * {@inheritDoc}
310       */
311      @Override()
312      public void toString(final StringBuilder buffer)
313      {
314        buffer.append("StartInteractiveTransactionExtendedResult(resultCode=");
315        buffer.append(getResultCode());
316    
317        final int messageID = getMessageID();
318        if (messageID >= 0)
319        {
320          buffer.append(", messageID=");
321          buffer.append(messageID);
322        }
323    
324        if (transactionID != null)
325        {
326          buffer.append(", transactionID='");
327          buffer.append(transactionID.stringValue());
328          buffer.append('\'');
329        }
330    
331        if (baseDNs != null)
332        {
333          buffer.append(", baseDNs={");
334          for (int i=0; i < baseDNs.size(); i++)
335          {
336            if (i > 0)
337            {
338              buffer.append(", ");
339            }
340    
341            buffer.append('\'');
342            buffer.append(baseDNs.get(i));
343            buffer.append('\'');
344          }
345          buffer.append('}');
346        }
347    
348        final String diagnosticMessage = getDiagnosticMessage();
349        if (diagnosticMessage != null)
350        {
351          buffer.append(", diagnosticMessage='");
352          buffer.append(diagnosticMessage);
353          buffer.append('\'');
354        }
355    
356        final String matchedDN = getMatchedDN();
357        if (matchedDN != null)
358        {
359          buffer.append(", matchedDN='");
360          buffer.append(matchedDN);
361          buffer.append('\'');
362        }
363    
364        final String[] referralURLs = getReferralURLs();
365        if (referralURLs.length > 0)
366        {
367          buffer.append(", referralURLs={");
368          for (int i=0; i < referralURLs.length; i++)
369          {
370            if (i > 0)
371            {
372              buffer.append(", ");
373            }
374    
375            buffer.append('\'');
376            buffer.append(referralURLs[i]);
377            buffer.append('\'');
378          }
379          buffer.append('}');
380        }
381    
382        final Control[] responseControls = getResponseControls();
383        if (responseControls.length > 0)
384        {
385          buffer.append(", responseControls={");
386          for (int i=0; i < responseControls.length; i++)
387          {
388            if (i > 0)
389            {
390              buffer.append(", ");
391            }
392    
393            buffer.append(responseControls[i]);
394          }
395          buffer.append('}');
396        }
397    
398        buffer.append(')');
399      }
400    }