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
027 import com.unboundid.asn1.ASN1Boolean;
028 import com.unboundid.asn1.ASN1Element;
029 import com.unboundid.asn1.ASN1OctetString;
030 import com.unboundid.asn1.ASN1Sequence;
031 import com.unboundid.ldap.sdk.Control;
032 import com.unboundid.ldap.sdk.LDAPException;
033 import com.unboundid.ldap.sdk.ResultCode;
034 import com.unboundid.ldap.sdk.unboundidds.extensions.
035 StartInteractiveTransactionExtendedRequest;
036 import com.unboundid.ldap.sdk.unboundidds.extensions.
037 StartInteractiveTransactionExtendedResult;
038 import com.unboundid.util.NotMutable;
039 import com.unboundid.util.ThreadSafety;
040 import com.unboundid.util.ThreadSafetyLevel;
041
042 import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*;
043 import static com.unboundid.util.StaticUtils.*;
044 import static com.unboundid.util.Validator.*;
045
046
047
048 /**
049 * <BLOCKQUOTE>
050 * <B>NOTE:</B> This class is part of the Commercial Edition of the UnboundID
051 * LDAP SDK for Java. It is not available for use in applications that
052 * include only the Standard Edition of the LDAP SDK, and is not supported for
053 * use in conjunction with non-UnboundID products.
054 * </BLOCKQUOTE>
055 * This class provides an implementation of the interactive transaction
056 * specification request control, which may be used to indicate that the
057 * associated operation is part of an interactive transaction. It may be used
058 * in conjunction with add, compare, delete, modify, modify DN, and search
059 * requests, as well as some types of extended requests. The transaction should
060 * be created with the start interactive transaction extended request, and the
061 * end interactive transaction extended request may be used to commit or abort
062 * the associated transaction.
063 * <BR><BR>
064 * The elements of the interactive transaction specification request control may
065 * include:
066 * <UL>
067 * <LI><CODE>txnID</CODE> -- The transaction ID for the transaction, which was
068 * obtained from a previous
069 * {@link StartInteractiveTransactionExtendedResult}.</LI>
070 * <LI><CODE>abortOnFailure</CODE> -- Indicates whether the transaction should
071 * be aborted if the request associated with this control does not
072 * complete successfully.</LI>
073 * <LI><CODE>writeLock</CODE> -- Indicates whether the target entry may be
074 * altered by this or a subsequent operation which is part of the
075 * transaction. It should generally be {@code false} only for read
076 * operations in which it is known that the target entry will not be
077 * altered by a subsequent operation.</LI>
078 * </UL>
079 * See the documentation for the
080 * {@link StartInteractiveTransactionExtendedRequest} class for an example of
081 * processing an interactive transaction.
082 */
083 @NotMutable()
084 @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
085 public final class InteractiveTransactionSpecificationRequestControl
086 extends Control
087 {
088 /**
089 * The OID (1.3.6.1.4.1.30221.2.5.4) for the interactive transaction
090 * specification request control.
091 */
092 public static final String INTERACTIVE_TRANSACTION_SPECIFICATION_REQUEST_OID =
093 "1.3.6.1.4.1.30221.2.5.4";
094
095
096
097 /**
098 * The BER type for the {@code txnID} element of the control value.
099 */
100 private static final byte TYPE_TXN_ID = (byte) 0x80;
101
102
103
104 /**
105 * The BER type for the {@code abortOnFailure} element of the control value.
106 */
107 private static final byte TYPE_ABORT_ON_FAILURE = (byte) 0x81;
108
109
110
111 /**
112 * The BER type for the {@code writeLock} element of the control value.
113 */
114 private static final byte TYPE_WRITE_LOCK = (byte) 0x82;
115
116
117
118 /**
119 * The serial version UID for this serializable class.
120 */
121 private static final long serialVersionUID = -6473934815135786621L;
122
123
124
125 // The transaction ID for the associated transaction.
126 private final ASN1OctetString transactionID;
127
128 // Indicates whether the transaction should be aborted if the associated
129 // operation does not complete successfully.
130 private final boolean abortOnFailure;
131
132 // Indicates whether the server should attempt to obtain a write lock on the
133 // target entry if the associated operation is a read operation.
134 private final boolean writeLock;
135
136
137
138 // This is an ugly hack to prevent checkstyle from complaining about imports
139 // for classes that are needed by javadoc @link elements but aren't otherwise
140 // used in the class. It appears that checkstyle does not recognize the use
141 // of these classes in javadoc @link elements so we must ensure that they are
142 // referenced elsewhere in the class to prevent checkstyle from complaining.
143 static
144 {
145 final StartInteractiveTransactionExtendedRequest r1 = null;
146 final StartInteractiveTransactionExtendedResult r2 = null;
147 }
148
149
150
151 /**
152 * Creates a new interactive transaction specification request control with
153 * the provided transaction ID. The server will attempt to keep the
154 * transaction active in the event of a failure and will obtain write locks on
155 * targeted entries.
156 *
157 * @param transactionID The transaction ID for the associated transaction,
158 * as obtained from the start interactive transaction
159 * extended operation. It must not be {@code null}.
160 */
161 public InteractiveTransactionSpecificationRequestControl(
162 final ASN1OctetString transactionID)
163 {
164 this(transactionID, false, true);
165 }
166
167
168
169 /**
170 * Creates a new interactive transaction specification request control with
171 * the provided information.
172 *
173 * @param transactionID The transaction ID for the associated transaction,
174 * as obtained from the start interactive transaction
175 * extended operation. It must not be {@code null}.
176 * @param abortOnFailure Indicates whether the transaction should be aborted
177 * if the associated operation does not complete
178 * successfully.
179 * @param writeLock Indicates whether the server should attempt to
180 * obtain a write lock on the target entry. This
181 * should only be {@code false} if the associated
182 * operation is a search or compare and it is known
183 * that the target entry will not be updated later in
184 * the transaction.
185 */
186 public InteractiveTransactionSpecificationRequestControl(
187 final ASN1OctetString transactionID, final boolean abortOnFailure,
188 final boolean writeLock)
189 {
190 super(INTERACTIVE_TRANSACTION_SPECIFICATION_REQUEST_OID, true,
191 encodeValue(transactionID, abortOnFailure, writeLock));
192
193 this.transactionID = transactionID;
194 this.abortOnFailure = abortOnFailure;
195 this.writeLock = writeLock;
196 }
197
198
199
200 /**
201 * Creates a new interactive transaction specification request control which
202 * is decoded from the provided generic control.
203 *
204 * @param control The generic control to be decoded as an interactive
205 * transaction specification request control.
206 *
207 * @throws LDAPException If the provided control cannot be decoded as an
208 * interactive transaction specification request
209 * control.
210 */
211 public InteractiveTransactionSpecificationRequestControl(
212 final Control control)
213 throws LDAPException
214 {
215 super(control);
216
217 if (! control.hasValue())
218 {
219 throw new LDAPException(ResultCode.DECODING_ERROR,
220 ERR_INT_TXN_REQUEST_NO_VALUE.get());
221 }
222
223 final ASN1Element[] elements;
224 try
225 {
226 final ASN1Element e = ASN1Element.decode(control.getValue().getValue());
227 elements = ASN1Sequence.decodeAsSequence(e).elements();
228 }
229 catch (Exception e)
230 {
231 throw new LDAPException(ResultCode.DECODING_ERROR,
232 ERR_INT_TXN_REQUEST_VALUE_NOT_SEQUENCE.get(e.getMessage()), e);
233 }
234
235 ASN1OctetString txnID = null;
236 boolean shouldAbortOnFailure = false;
237 boolean shouldWriteLock = true;
238
239 for (final ASN1Element element : elements)
240 {
241 switch (element.getType())
242 {
243 case TYPE_TXN_ID:
244 txnID = ASN1OctetString.decodeAsOctetString(element);
245 break;
246 case TYPE_ABORT_ON_FAILURE:
247 try
248 {
249 shouldAbortOnFailure =
250 ASN1Boolean.decodeAsBoolean(element).booleanValue();
251 }
252 catch (Exception e)
253 {
254 throw new LDAPException(ResultCode.DECODING_ERROR,
255 ERR_INT_TXN_REQUEST_ABORT_ON_FAILURE_NOT_BOOLEAN.get(
256 e.getMessage()), e);
257 }
258 break;
259 case TYPE_WRITE_LOCK:
260 try
261 {
262 shouldWriteLock =
263 ASN1Boolean.decodeAsBoolean(element).booleanValue();
264 }
265 catch (Exception e)
266 {
267 throw new LDAPException(ResultCode.DECODING_ERROR,
268 ERR_INT_TXN_REQUEST_WRITE_LOCK_NOT_BOOLEAN.get(e.getMessage()),
269 e);
270 }
271 break;
272 default:
273 throw new LDAPException(ResultCode.DECODING_ERROR,
274 ERR_INT_TXN_REQUEST_INVALID_ELEMENT_TYPE.get(
275 toHex(element.getType())));
276 }
277 }
278
279 if (txnID == null)
280 {
281 throw new LDAPException(ResultCode.DECODING_ERROR,
282 ERR_INT_TXN_REQUEST_NO_TXN_ID.get());
283 }
284
285 transactionID = txnID;
286 abortOnFailure = shouldAbortOnFailure;
287 writeLock = shouldWriteLock;
288 }
289
290
291
292 /**
293 * Encodes the provided information into an ASN.1 octet string suitable for
294 * use as the value of this control.
295 *
296 * @param transactionID The transaction ID for the associated transaction,
297 * as obtained from the start interactive transaction
298 * extended operation. It must not be {@code null}.
299 * @param abortOnFailure Indicates whether the transaction should be aborted
300 * if the associated operation does not complete
301 * successfully.
302 * @param writeLock Indicates whether the server should attempt to
303 * obtain a write lock on the target entry. This
304 * should only be {@code false} if the associated
305 * operation is a search or compare and it is known
306 * that the target entry will not be updated later in
307 * the transaction.
308 *
309 * @return The ASN.1 octet string containing the encoded value for this
310 * control.
311 */
312 private static ASN1OctetString encodeValue(
313 final ASN1OctetString transactionID,
314 final boolean abortOnFailure, final boolean writeLock)
315 {
316 ensureNotNull(transactionID);
317
318 final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(3);
319 elements.add(new ASN1OctetString(TYPE_TXN_ID, transactionID.getValue()));
320
321 if (abortOnFailure)
322 {
323 elements.add(new ASN1Boolean(TYPE_ABORT_ON_FAILURE, abortOnFailure));
324 }
325
326 if (! writeLock)
327 {
328 elements.add(new ASN1Boolean(TYPE_WRITE_LOCK, writeLock));
329 }
330
331 return new ASN1OctetString(new ASN1Sequence(elements).encode());
332 }
333
334
335
336 /**
337 * Retrieves the transaction ID for the associated transaction.
338 *
339 * @return The transaction ID for the associated transaction.
340 */
341 public ASN1OctetString getTransactionID()
342 {
343 return transactionID;
344 }
345
346
347
348 /**
349 * Indicates whether the transaction should be aborted if the associated
350 * operation does not complete successfully.
351 *
352 * @return {@code true} if the transaction should be aborted if the
353 * associated operation does not complete successfully, or
354 * {@code false} if the server should attempt to keep the transaction
355 * active if the associated operation does not complete successfully.
356 */
357 public boolean abortOnFailure()
358 {
359 return abortOnFailure;
360 }
361
362
363
364 /**
365 * Indicates whether the server should attempt to obtain a write lock on
366 * entries targeted by the associated operation.
367 *
368 * @return {@code true} if the server should attempt to obtain a write lock
369 * on entries targeted by the associated operation, or {@code false}
370 * if a read lock is acceptable as the entries are not expected to
371 * be altered later in the transaction.
372 */
373 public boolean writeLock()
374 {
375 return writeLock;
376 }
377
378
379
380 /**
381 * {@inheritDoc}
382 */
383 @Override()
384 public String getControlName()
385 {
386 return INFO_CONTROL_NAME_INTERACTIVE_TXN_REQUEST.get();
387 }
388
389
390
391 /**
392 * {@inheritDoc}
393 */
394 @Override()
395 public void toString(final StringBuilder buffer)
396 {
397 buffer.append("InteractiveTransactionSpecificationRequestControl(" +
398 "transactionID='");
399 buffer.append(transactionID.stringValue());
400 buffer.append("', abortOnFailure=");
401 buffer.append(abortOnFailure);
402 buffer.append(", writeLock=");
403 buffer.append(writeLock);
404 buffer.append(')');
405 }
406 }