001/* 002 * Copyright 2008-2019 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2015-2019 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.Debug; 034import com.unboundid.util.NotMutable; 035import com.unboundid.util.StaticUtils; 036import com.unboundid.util.ThreadSafety; 037import com.unboundid.util.ThreadSafetyLevel; 038import com.unboundid.util.Validator; 039 040import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*; 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 055 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 056 * for proprietary functionality or for external specifications that are not 057 * considered stable or mature enough to be guaranteed to work in an 058 * interoperable way with 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( 191 StaticUtils.toHex(e.getType()))); 192 } 193 } 194 } 195 catch (final LDAPException le) 196 { 197 Debug.debugException(le); 198 throw le; 199 } 200 catch (final Exception e) 201 { 202 Debug.debugException(e); 203 throw new LDAPException(ResultCode.DECODING_ERROR, 204 ERR_END_INT_TXN_REQUEST_CANNOT_DECODE.get(e), e); 205 } 206 207 if (txnID == null) 208 { 209 throw new LDAPException(ResultCode.DECODING_ERROR, 210 ERR_END_INT_TXN_REQUEST_NO_TXN_ID.get()); 211 } 212 213 transactionID = txnID; 214 commit = shouldCommit; 215 } 216 217 218 219 /** 220 * Generates the value to include in this extended request. 221 * 222 * @param transactionID The transaction ID for the transaction to commit or 223 * abort. It must not be {@code null}. 224 * @param commit {@code true} if the transaction should be committed, 225 * or {@code false} if the transaction should be 226 * aborted. 227 * 228 * @return The ASN.1 octet string containing the encoded request value. 229 */ 230 private static ASN1OctetString 231 encodeValue(final ASN1OctetString transactionID, 232 final boolean commit) 233 { 234 Validator.ensureNotNull(transactionID); 235 236 final ASN1Element[] valueElements; 237 if (commit) 238 { 239 valueElements = new ASN1Element[] 240 { 241 new ASN1OctetString(TYPE_TXN_ID, transactionID.getValue()) 242 }; 243 } 244 else 245 { 246 valueElements = new ASN1Element[] 247 { 248 new ASN1OctetString(TYPE_TXN_ID, transactionID.getValue()), 249 new ASN1Boolean(TYPE_COMMIT, commit) 250 }; 251 } 252 253 return new ASN1OctetString(new ASN1Sequence(valueElements).encode()); 254 } 255 256 257 258 /** 259 * Retrieves the transaction ID for the transaction to commit or abort. 260 * 261 * @return The transaction ID for the transaction to commit or abort. 262 */ 263 public ASN1OctetString getTransactionID() 264 { 265 return transactionID; 266 } 267 268 269 270 /** 271 * Indicates whether the transaction should be committed or aborted. 272 * 273 * @return {@code true} if the transaction should be committed, or 274 * {@code false} if it should be aborted. 275 */ 276 public boolean commit() 277 { 278 return commit; 279 } 280 281 282 283 /** 284 * {@inheritDoc} 285 */ 286 @Override() 287 public EndInteractiveTransactionExtendedRequest duplicate() 288 { 289 return duplicate(getControls()); 290 } 291 292 293 294 /** 295 * {@inheritDoc} 296 */ 297 @Override() 298 public EndInteractiveTransactionExtendedRequest duplicate( 299 final Control[] controls) 300 { 301 final EndInteractiveTransactionExtendedRequest r = 302 new EndInteractiveTransactionExtendedRequest(transactionID, commit, 303 controls); 304 r.setResponseTimeoutMillis(getResponseTimeoutMillis(null)); 305 return r; 306 } 307 308 309 310 /** 311 * {@inheritDoc} 312 */ 313 @Override() 314 public String getExtendedRequestName() 315 { 316 return INFO_EXTENDED_REQUEST_NAME_END_INTERACTIVE_TXN.get(); 317 } 318 319 320 321 /** 322 * {@inheritDoc} 323 */ 324 @Override() 325 public void toString(final StringBuilder buffer) 326 { 327 buffer.append("EndInteractiveTransactionExtendedRequest(transactionID='"); 328 buffer.append(transactionID.stringValue()); 329 buffer.append("', commit="); 330 buffer.append(commit); 331 332 final Control[] controls = getControls(); 333 if (controls.length > 0) 334 { 335 buffer.append("controls={"); 336 for (int i=0; i < controls.length; i++) 337 { 338 if (i > 0) 339 { 340 buffer.append(", "); 341 } 342 343 buffer.append(controls[i]); 344 } 345 buffer.append('}'); 346 } 347 348 buffer.append(')'); 349 } 350}