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 }