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.controls;
022
023
024
025 import java.util.ArrayList;
026 import java.util.Collections;
027 import java.util.List;
028
029 import com.unboundid.asn1.ASN1Boolean;
030 import com.unboundid.asn1.ASN1Element;
031 import com.unboundid.asn1.ASN1OctetString;
032 import com.unboundid.asn1.ASN1Sequence;
033 import com.unboundid.ldap.sdk.Control;
034 import com.unboundid.ldap.sdk.DecodeableControl;
035 import com.unboundid.ldap.sdk.LDAPException;
036 import com.unboundid.ldap.sdk.LDAPResult;
037 import com.unboundid.ldap.sdk.ResultCode;
038 import com.unboundid.ldap.sdk.unboundidds.extensions.
039 StartInteractiveTransactionExtendedRequest;
040 import com.unboundid.util.NotMutable;
041 import com.unboundid.util.ThreadSafety;
042 import com.unboundid.util.ThreadSafetyLevel;
043
044 import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*;
045 import static com.unboundid.util.StaticUtils.*;
046
047
048
049 /**
050 * <BLOCKQUOTE>
051 * <B>NOTE:</B> This class is part of the Commercial Edition of the UnboundID
052 * LDAP SDK for Java. It is not available for use in applications that
053 * include only the Standard Edition of the LDAP SDK, and is not supported for
054 * use in conjunction with non-UnboundID products.
055 * </BLOCKQUOTE>
056 * This class defines an interactive transaction specification response control,
057 * which will be included in the server's response to an operation that included
058 * the {@link InteractiveTransactionSpecificationRequestControl}. It provides
059 * information about the state of the transaction, which may include:
060 * <UL>
061 * <LI><CODE>transactionValid</CODE> -- Indicates whether the transaction is
062 * still valid in the server. This should be checked if the associated
063 * operation did not complete successfully.</LI>
064 * <LI><CODE>baseDNs</CODE> -- This may specify the set of base DNs below
065 * which the client is allowed to request operations as part of this
066 * transaction. It may be absent if there are no restrictions on which
067 * base DNs may be used, or if it has not changed since the last
068 * response within this transaction.</LI>
069 * </UL>
070 * See the documentation in the
071 * {@link StartInteractiveTransactionExtendedRequest} class for an example of
072 * processing interactive transactions.
073 */
074 @NotMutable()
075 @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
076 public final class InteractiveTransactionSpecificationResponseControl
077 extends Control
078 implements DecodeableControl
079 {
080 /**
081 * The OID (1.3.6.1.4.1.30221.2.5.4) for the interactive transaction
082 * specification response control.
083 */
084 public static final String
085 INTERACTIVE_TRANSACTION_SPECIFICATION_RESPONSE_OID =
086 "1.3.6.1.4.1.30221.2.5.4";
087
088
089
090 /**
091 * The BER type for the {@code transactionValid} element of the control value.
092 */
093 private static final byte TYPE_TXN_VALID = (byte) 0x80;
094
095
096
097 /**
098 * The BER type for the {@code baseDNs} element of the control value.
099 */
100 private static final byte TYPE_BASE_DNS = (byte) 0xA1;
101
102
103
104 /**
105 * The serial version UID for this serializable class.
106 */
107 private static final long serialVersionUID = -4323085263241417543L;
108
109
110
111 // The flag that indicates whether the associated transaction is still valid.
112 private final boolean transactionValid;
113
114 // The set of base DNs that may be targeted by this transaction.
115 private final List<String> baseDNs;
116
117
118
119 // This is an ugly hack to prevent checkstyle from complaining about imports
120 // for classes that are needed by javadoc @link elements but aren't otherwise
121 // used in the class. It appears that checkstyle does not recognize the use
122 // of these classes in javadoc @link elements so we must ensure that they are
123 // referenced elsewhere in the class to prevent checkstyle from complaining.
124 static
125 {
126 final StartInteractiveTransactionExtendedRequest r = null;
127 }
128
129
130
131 /**
132 * Creates a new empty control instance that is intended to be used only for
133 * decoding controls via the {@code DecodeableControl} interface.
134 */
135 InteractiveTransactionSpecificationResponseControl()
136 {
137 transactionValid = false;
138 baseDNs = null;
139 }
140
141
142
143 /**
144 * Creates a new interactive transaction specification response control with
145 * the provided information. It will not be marked critical.
146 *
147 * @param transactionValid Indicates whether the associated transaction is
148 * still valid.
149 * @param baseDNs The set of base DNs that may be targeted over the
150 * course of the transaction. It may be
151 * {@code null} if there are no restrictions or the
152 * set of restrictions has not changed since the
153 * last response.
154 */
155 public InteractiveTransactionSpecificationResponseControl(
156 final boolean transactionValid, final List<String> baseDNs)
157 {
158 super(INTERACTIVE_TRANSACTION_SPECIFICATION_RESPONSE_OID, false,
159 encodeValue(transactionValid, baseDNs));
160
161 this.transactionValid = transactionValid;
162
163 if (baseDNs == null)
164 {
165 this.baseDNs = null;
166 }
167 else
168 {
169 this.baseDNs =
170 Collections.unmodifiableList(new ArrayList<String>(baseDNs));
171 }
172 }
173
174
175
176 /**
177 * Creates a new interactive transaction specification response control with
178 * the provided information.
179 *
180 * @param oid The OID for the control.
181 * @param isCritical Indicates whether the control should be marked
182 * critical.
183 * @param value The encoded value for the control. This may be
184 * {@code null} if no value was provided.
185 *
186 * @throws LDAPException If the provided control cannot be decoded as an
187 * interactive transaction specification response
188 * control.
189 */
190 public InteractiveTransactionSpecificationResponseControl(final String oid,
191 final boolean isCritical, final ASN1OctetString value)
192 throws LDAPException
193 {
194 super(oid, isCritical, value);
195
196 if (value == null)
197 {
198 throw new LDAPException(ResultCode.DECODING_ERROR,
199 ERR_INT_TXN_RESPONSE_NO_VALUE.get());
200 }
201
202 final ASN1Element[] elements;
203 try
204 {
205 final ASN1Element valueElement = ASN1Element.decode(value.getValue());
206 elements = ASN1Sequence.decodeAsSequence(valueElement).elements();
207 }
208 catch(Exception e)
209 {
210 throw new LDAPException(ResultCode.DECODING_ERROR,
211 ERR_INT_TXN_RESPONSE_VALUE_NOT_SEQUENCE.get(
212 e.getMessage()), e);
213 }
214
215 Boolean isValid = null;
216 List<String> baseDNList = null;
217
218 for (final ASN1Element element : elements)
219 {
220 switch (element.getType())
221 {
222 case TYPE_TXN_VALID:
223 try
224 {
225 isValid = ASN1Boolean.decodeAsBoolean(element).booleanValue();
226 }
227 catch (Exception e)
228 {
229 throw new LDAPException(ResultCode.DECODING_ERROR,
230 ERR_INT_TXN_RESPONSE_TXN_VALID_NOT_BOOLEAN.get(e.getMessage()),
231 e);
232 }
233 break;
234 case TYPE_BASE_DNS:
235 try
236 {
237 final ASN1Sequence s = ASN1Sequence.decodeAsSequence(element);
238 baseDNList = new ArrayList<String>(s.elements().length);
239 for (final ASN1Element e : s.elements())
240 {
241 baseDNList.add(
242 ASN1OctetString.decodeAsOctetString(e).stringValue());
243 }
244 }
245 catch (Exception e)
246 {
247 throw new LDAPException(ResultCode.DECODING_ERROR,
248 ERR_INT_TXN_RESPONSE_BASE_DNS_NOT_SEQUENCE.get(e.getMessage()),
249 e);
250 }
251 break;
252 default:
253 throw new LDAPException(ResultCode.DECODING_ERROR,
254 ERR_INT_TXN_RESPONSE_INVALID_ELEMENT_TYPE.get(
255 toHex(element.getType())));
256 }
257 }
258
259 if (isValid == null)
260 {
261 throw new LDAPException(ResultCode.DECODING_ERROR,
262 ERR_INT_TXN_RESPONSE_NO_TXN_VALID.get());
263 }
264
265 transactionValid = isValid;
266
267 if (baseDNList == null)
268 {
269 baseDNs = null;
270 }
271 else
272 {
273 baseDNs = Collections.unmodifiableList(baseDNList);
274 }
275 }
276
277
278
279 /**
280 * Encodes the provided information into an ASN.1 octet string suitable for
281 * use as the value of this control.
282 *
283 * @param transactionValid Indicates whether the associated transaction is
284 * still valid.
285 * @param baseDNs The set of base DNs that may be targeted over the
286 * course of the transaction. It may be
287 * {@code null} if there are no restrictions or the
288 * set of restrictions has not changed since the
289 * last response.
290 *
291 * @return The ASN1 octet string that may be used as the control value.
292 */
293 private static ASN1OctetString encodeValue(final boolean transactionValid,
294 final List<String> baseDNs)
295 {
296 final ASN1Element[] elements;
297 if (baseDNs == null)
298 {
299 elements = new ASN1Element[]
300 {
301 new ASN1Boolean(TYPE_TXN_VALID, transactionValid)
302 };
303 }
304 else
305 {
306 final ASN1Element[] baseDNElements = new ASN1Element[baseDNs.size()];
307 for (int i=0; i < baseDNElements.length; i++)
308 {
309 baseDNElements[i] = new ASN1OctetString(baseDNs.get(i));
310 }
311
312 elements = new ASN1Element[]
313 {
314 new ASN1Boolean(TYPE_TXN_VALID, transactionValid),
315 new ASN1Sequence(TYPE_BASE_DNS, baseDNElements)
316 };
317 }
318
319 return new ASN1OctetString(new ASN1Sequence(elements).encode());
320 }
321
322
323
324 /**
325 * {@inheritDoc}
326 */
327 public InteractiveTransactionSpecificationResponseControl decodeControl(
328 final String oid, final boolean isCritical,
329 final ASN1OctetString value)
330 throws LDAPException
331 {
332 return new InteractiveTransactionSpecificationResponseControl(oid,
333 isCritical, value);
334 }
335
336
337
338 /**
339 * Extracts an interactive transaction specification response control from the
340 * provided result.
341 *
342 * @param result The result from which to retrieve the interactive
343 * transaction specification response control.
344 *
345 * @return The interactive transaction specification response control
346 * contained in the provided result, or {@code null} if the result
347 * did not contain an interactive transaction specification response
348 * control.
349 *
350 * @throws LDAPException If a problem is encountered while attempting to
351 * decode the interactive transaction specification
352 * response control contained in the provided result.
353 */
354 public static InteractiveTransactionSpecificationResponseControl
355 get(final LDAPResult result)
356 throws LDAPException
357 {
358 final Control c = result.getResponseControl(
359 INTERACTIVE_TRANSACTION_SPECIFICATION_RESPONSE_OID);
360 if (c == null)
361 {
362 return null;
363 }
364
365 if (c instanceof InteractiveTransactionSpecificationResponseControl)
366 {
367 return (InteractiveTransactionSpecificationResponseControl) c;
368 }
369 else
370 {
371 return new InteractiveTransactionSpecificationResponseControl(c.getOID(),
372 c.isCritical(), c.getValue());
373 }
374 }
375
376
377
378 /**
379 * Indicates whether the associated transaction is still valid on the server.
380 *
381 * @return {@code true} if the associated transaction is still valid on the
382 * server and may be used for future operations, or {@code false} if
383 * the transaction has been aborted and may no longer be used.
384 */
385 public boolean transactionValid()
386 {
387 return transactionValid;
388 }
389
390
391
392 /**
393 * Retrieves the set of base DNs below which operations which are part of the
394 * transaction may be performed.
395 *
396 * @return The set of base DNs below which operations may be performed as
397 * part of the transaction, or {@code null} if there are no
398 * restrictions or if the set of restrictions has not changed since
399 * the last response.
400 */
401 public List<String> getBaseDNs()
402 {
403 return baseDNs;
404 }
405
406
407
408 /**
409 * {@inheritDoc}
410 */
411 @Override()
412 public String getControlName()
413 {
414 return INFO_CONTROL_NAME_INTERACTIVE_TXN_RESPONSE.get();
415 }
416
417
418
419 /**
420 * {@inheritDoc}
421 */
422 @Override()
423 public void toString(final StringBuilder buffer)
424 {
425 buffer.append("InteractiveTransactionSpecificationResponseControl(");
426 buffer.append("transactionValid=");
427 buffer.append(transactionValid);
428 buffer.append(", baseDNs=");
429 if (baseDNs == null)
430 {
431 buffer.append("null");
432 }
433 else
434 {
435 buffer.append('{');
436 for (int i=0; i < baseDNs.size(); i++)
437 {
438 if (i > 0)
439 {
440 buffer.append(", ");
441 }
442
443 buffer.append('\'');
444 buffer.append(baseDNs.get(i));
445 buffer.append('\'');
446 }
447 buffer.append('}');
448 }
449
450 buffer.append(", isCritical=");
451 buffer.append(isCritical());
452 buffer.append(')');
453 }
454 }