001/* 002 * Copyright 2008-2020 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2008-2020 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2008-2020 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.ldap.sdk.unboundidds.controls; 037 038 039 040import java.util.ArrayList; 041import java.util.Collections; 042import java.util.List; 043 044import com.unboundid.asn1.ASN1Boolean; 045import com.unboundid.asn1.ASN1Element; 046import com.unboundid.asn1.ASN1OctetString; 047import com.unboundid.asn1.ASN1Sequence; 048import com.unboundid.ldap.sdk.Control; 049import com.unboundid.ldap.sdk.DecodeableControl; 050import com.unboundid.ldap.sdk.LDAPException; 051import com.unboundid.ldap.sdk.LDAPResult; 052import com.unboundid.ldap.sdk.ResultCode; 053import com.unboundid.ldap.sdk.unboundidds.extensions. 054 StartInteractiveTransactionExtendedRequest; 055import com.unboundid.util.NotMutable; 056import com.unboundid.util.NotNull; 057import com.unboundid.util.Nullable; 058import com.unboundid.util.StaticUtils; 059import com.unboundid.util.ThreadSafety; 060import com.unboundid.util.ThreadSafetyLevel; 061 062import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*; 063 064 065 066/** 067 * This class defines an interactive transaction specification response control, 068 * which will be included in the server's response to an operation that included 069 * the {@link InteractiveTransactionSpecificationRequestControl}. 070 * <BR> 071 * <BLOCKQUOTE> 072 * <B>NOTE:</B> This class, and other classes within the 073 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 074 * supported for use against Ping Identity, UnboundID, and 075 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 076 * for proprietary functionality or for external specifications that are not 077 * considered stable or mature enough to be guaranteed to work in an 078 * interoperable way with other types of LDAP servers. 079 * </BLOCKQUOTE> 080 * <BR> 081 * It provides information about the state of the transaction, which may 082 * include: 083 * <UL> 084 * <LI><CODE>transactionValid</CODE> -- Indicates whether the transaction is 085 * still valid in the server. This should be checked if the associated 086 * operation did not complete successfully.</LI> 087 * <LI><CODE>baseDNs</CODE> -- This may specify the set of base DNs below 088 * which the client is allowed to request operations as part of this 089 * transaction. It may be absent if there are no restrictions on which 090 * base DNs may be used, or if it has not changed since the last 091 * response within this transaction.</LI> 092 * </UL> 093 * See the documentation in the 094 * {@link StartInteractiveTransactionExtendedRequest} class for an example of 095 * processing interactive transactions. 096 */ 097@NotMutable() 098@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 099public final class InteractiveTransactionSpecificationResponseControl 100 extends Control 101 implements DecodeableControl 102{ 103 /** 104 * The OID (1.3.6.1.4.1.30221.2.5.4) for the interactive transaction 105 * specification response control. 106 */ 107 @NotNull public static final String 108 INTERACTIVE_TRANSACTION_SPECIFICATION_RESPONSE_OID = 109 "1.3.6.1.4.1.30221.2.5.4"; 110 111 112 113 /** 114 * The BER type for the {@code transactionValid} element of the control value. 115 */ 116 private static final byte TYPE_TXN_VALID = (byte) 0x80; 117 118 119 120 /** 121 * The BER type for the {@code baseDNs} element of the control value. 122 */ 123 private static final byte TYPE_BASE_DNS = (byte) 0xA1; 124 125 126 127 /** 128 * The serial version UID for this serializable class. 129 */ 130 private static final long serialVersionUID = -4323085263241417543L; 131 132 133 134 // The flag that indicates whether the associated transaction is still valid. 135 private final boolean transactionValid; 136 137 // The set of base DNs that may be targeted by this transaction. 138 @Nullable private final List<String> baseDNs; 139 140 141 142 /** 143 * Creates a new empty control instance that is intended to be used only for 144 * decoding controls via the {@code DecodeableControl} interface. 145 */ 146 InteractiveTransactionSpecificationResponseControl() 147 { 148 transactionValid = false; 149 baseDNs = null; 150 } 151 152 153 154 /** 155 * Creates a new interactive transaction specification response control with 156 * the provided information. It will not be marked critical. 157 * 158 * @param transactionValid Indicates whether the associated transaction is 159 * still valid. 160 * @param baseDNs The set of base DNs that may be targeted over the 161 * course of the transaction. It may be 162 * {@code null} if there are no restrictions or the 163 * set of restrictions has not changed since the 164 * last response. 165 */ 166 public InteractiveTransactionSpecificationResponseControl( 167 final boolean transactionValid, 168 @Nullable final List<String> baseDNs) 169 { 170 super(INTERACTIVE_TRANSACTION_SPECIFICATION_RESPONSE_OID, false, 171 encodeValue(transactionValid, baseDNs)); 172 173 this.transactionValid = transactionValid; 174 175 if (baseDNs == null) 176 { 177 this.baseDNs = null; 178 } 179 else 180 { 181 this.baseDNs = 182 Collections.unmodifiableList(new ArrayList<>(baseDNs)); 183 } 184 } 185 186 187 188 /** 189 * Creates a new interactive transaction specification response control with 190 * the provided information. 191 * 192 * @param oid The OID for the control. 193 * @param isCritical Indicates whether the control should be marked 194 * critical. 195 * @param value The encoded value for the control. This may be 196 * {@code null} if no value was provided. 197 * 198 * @throws LDAPException If the provided control cannot be decoded as an 199 * interactive transaction specification response 200 * control. 201 */ 202 public InteractiveTransactionSpecificationResponseControl( 203 @NotNull final String oid, 204 final boolean isCritical, 205 @Nullable final ASN1OctetString value) 206 throws LDAPException 207 { 208 super(oid, isCritical, value); 209 210 if (value == null) 211 { 212 throw new LDAPException(ResultCode.DECODING_ERROR, 213 ERR_INT_TXN_RESPONSE_NO_VALUE.get()); 214 } 215 216 final ASN1Element[] elements; 217 try 218 { 219 final ASN1Element valueElement = ASN1Element.decode(value.getValue()); 220 elements = ASN1Sequence.decodeAsSequence(valueElement).elements(); 221 } 222 catch (final Exception e) 223 { 224 throw new LDAPException(ResultCode.DECODING_ERROR, 225 ERR_INT_TXN_RESPONSE_VALUE_NOT_SEQUENCE.get( 226 e.getMessage()), e); 227 } 228 229 Boolean isValid = null; 230 List<String> baseDNList = null; 231 232 for (final ASN1Element element : elements) 233 { 234 switch (element.getType()) 235 { 236 case TYPE_TXN_VALID: 237 try 238 { 239 isValid = ASN1Boolean.decodeAsBoolean(element).booleanValue(); 240 } 241 catch (final Exception e) 242 { 243 throw new LDAPException(ResultCode.DECODING_ERROR, 244 ERR_INT_TXN_RESPONSE_TXN_VALID_NOT_BOOLEAN.get(e.getMessage()), 245 e); 246 } 247 break; 248 case TYPE_BASE_DNS: 249 try 250 { 251 final ASN1Sequence s = ASN1Sequence.decodeAsSequence(element); 252 baseDNList = new ArrayList<>(s.elements().length); 253 for (final ASN1Element e : s.elements()) 254 { 255 baseDNList.add( 256 ASN1OctetString.decodeAsOctetString(e).stringValue()); 257 } 258 } 259 catch (final Exception e) 260 { 261 throw new LDAPException(ResultCode.DECODING_ERROR, 262 ERR_INT_TXN_RESPONSE_BASE_DNS_NOT_SEQUENCE.get(e.getMessage()), 263 e); 264 } 265 break; 266 default: 267 throw new LDAPException(ResultCode.DECODING_ERROR, 268 ERR_INT_TXN_RESPONSE_INVALID_ELEMENT_TYPE.get( 269 StaticUtils.toHex(element.getType()))); 270 } 271 } 272 273 if (isValid == null) 274 { 275 throw new LDAPException(ResultCode.DECODING_ERROR, 276 ERR_INT_TXN_RESPONSE_NO_TXN_VALID.get()); 277 } 278 279 transactionValid = isValid; 280 281 if (baseDNList == null) 282 { 283 baseDNs = null; 284 } 285 else 286 { 287 baseDNs = Collections.unmodifiableList(baseDNList); 288 } 289 } 290 291 292 293 /** 294 * Encodes the provided information into an ASN.1 octet string suitable for 295 * use as the value of this control. 296 * 297 * @param transactionValid Indicates whether the associated transaction is 298 * still valid. 299 * @param baseDNs The set of base DNs that may be targeted over the 300 * course of the transaction. It may be 301 * {@code null} if there are no restrictions or the 302 * set of restrictions has not changed since the 303 * last response. 304 * 305 * @return The ASN1 octet string that may be used as the control value. 306 */ 307 @NotNull() 308 private static ASN1OctetString encodeValue(final boolean transactionValid, 309 @Nullable final List<String> baseDNs) 310 { 311 final ASN1Element[] elements; 312 if (baseDNs == null) 313 { 314 elements = new ASN1Element[] 315 { 316 new ASN1Boolean(TYPE_TXN_VALID, transactionValid) 317 }; 318 } 319 else 320 { 321 final ASN1Element[] baseDNElements = new ASN1Element[baseDNs.size()]; 322 for (int i=0; i < baseDNElements.length; i++) 323 { 324 baseDNElements[i] = new ASN1OctetString(baseDNs.get(i)); 325 } 326 327 elements = new ASN1Element[] 328 { 329 new ASN1Boolean(TYPE_TXN_VALID, transactionValid), 330 new ASN1Sequence(TYPE_BASE_DNS, baseDNElements) 331 }; 332 } 333 334 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 335 } 336 337 338 339 /** 340 * {@inheritDoc} 341 */ 342 @Override() 343 @NotNull() 344 public InteractiveTransactionSpecificationResponseControl decodeControl( 345 @NotNull final String oid, final boolean isCritical, 346 @Nullable final ASN1OctetString value) 347 throws LDAPException 348 { 349 return new InteractiveTransactionSpecificationResponseControl(oid, 350 isCritical, value); 351 } 352 353 354 355 /** 356 * Extracts an interactive transaction specification response control from the 357 * provided result. 358 * 359 * @param result The result from which to retrieve the interactive 360 * transaction specification response control. 361 * 362 * @return The interactive transaction specification response control 363 * contained in the provided result, or {@code null} if the result 364 * did not contain an interactive transaction specification response 365 * control. 366 * 367 * @throws LDAPException If a problem is encountered while attempting to 368 * decode the interactive transaction specification 369 * response control contained in the provided result. 370 */ 371 @Nullable() 372 public static InteractiveTransactionSpecificationResponseControl get( 373 @NotNull final LDAPResult result) 374 throws LDAPException 375 { 376 final Control c = result.getResponseControl( 377 INTERACTIVE_TRANSACTION_SPECIFICATION_RESPONSE_OID); 378 if (c == null) 379 { 380 return null; 381 } 382 383 if (c instanceof InteractiveTransactionSpecificationResponseControl) 384 { 385 return (InteractiveTransactionSpecificationResponseControl) c; 386 } 387 else 388 { 389 return new InteractiveTransactionSpecificationResponseControl(c.getOID(), 390 c.isCritical(), c.getValue()); 391 } 392 } 393 394 395 396 /** 397 * Indicates whether the associated transaction is still valid on the server. 398 * 399 * @return {@code true} if the associated transaction is still valid on the 400 * server and may be used for future operations, or {@code false} if 401 * the transaction has been aborted and may no longer be used. 402 */ 403 public boolean transactionValid() 404 { 405 return transactionValid; 406 } 407 408 409 410 /** 411 * Retrieves the set of base DNs below which operations which are part of the 412 * transaction may be performed. 413 * 414 * @return The set of base DNs below which operations may be performed as 415 * part of the transaction, or {@code null} if there are no 416 * restrictions or if the set of restrictions has not changed since 417 * the last response. 418 */ 419 @Nullable() 420 public List<String> getBaseDNs() 421 { 422 return baseDNs; 423 } 424 425 426 427 /** 428 * {@inheritDoc} 429 */ 430 @Override() 431 @NotNull() 432 public String getControlName() 433 { 434 return INFO_CONTROL_NAME_INTERACTIVE_TXN_RESPONSE.get(); 435 } 436 437 438 439 /** 440 * {@inheritDoc} 441 */ 442 @Override() 443 public void toString(@NotNull final StringBuilder buffer) 444 { 445 buffer.append("InteractiveTransactionSpecificationResponseControl("); 446 buffer.append("transactionValid="); 447 buffer.append(transactionValid); 448 buffer.append(", baseDNs="); 449 if (baseDNs == null) 450 { 451 buffer.append("null"); 452 } 453 else 454 { 455 buffer.append('{'); 456 for (int i=0; i < baseDNs.size(); i++) 457 { 458 if (i > 0) 459 { 460 buffer.append(", "); 461 } 462 463 buffer.append('\''); 464 buffer.append(baseDNs.get(i)); 465 buffer.append('\''); 466 } 467 buffer.append('}'); 468 } 469 470 buffer.append(", isCritical="); 471 buffer.append(isCritical()); 472 buffer.append(')'); 473 } 474}