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