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.extensions; 022 023 024 025 import java.util.ArrayList; 026 import java.util.Collections; 027 import java.util.List; 028 029 import com.unboundid.asn1.ASN1Element; 030 import com.unboundid.asn1.ASN1OctetString; 031 import com.unboundid.asn1.ASN1Sequence; 032 import com.unboundid.ldap.sdk.Control; 033 import com.unboundid.ldap.sdk.ExtendedResult; 034 import com.unboundid.ldap.sdk.LDAPException; 035 import com.unboundid.ldap.sdk.ResultCode; 036 import com.unboundid.util.NotMutable; 037 import com.unboundid.util.ThreadSafety; 038 import com.unboundid.util.ThreadSafetyLevel; 039 040 import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*; 041 import static com.unboundid.util.Debug.*; 042 import static com.unboundid.util.StaticUtils.*; 043 044 045 046 /** 047 * <BLOCKQUOTE> 048 * <B>NOTE:</B> This class is part of the Commercial Edition of the UnboundID 049 * LDAP SDK for Java. It is not available for use in applications that 050 * include only the Standard Edition of the LDAP SDK, and is not supported for 051 * use in conjunction with non-UnboundID products. 052 * </BLOCKQUOTE> 053 * This class implements a data structure for storing the information from an 054 * extended result for the start interactive transaction extended request. It 055 * is able to decode a generic extended result to extract the transaction ID and 056 * base DNs that it may contain, if the operation was successful. 057 * <BR><BR> 058 * See the documentation for the 059 * {@link StartInteractiveTransactionExtendedRequest} class for an example that 060 * demonstrates the use of interactive transactions. 061 */ 062 @NotMutable() 063 @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 064 public final class StartInteractiveTransactionExtendedResult 065 extends ExtendedResult 066 { 067 /** 068 * The BER type for the {@code txnID} element of the response. 069 */ 070 private static final byte TYPE_TXN_ID = (byte) 0x80; 071 072 073 074 /** 075 * The BER type for the {@code baseDNs} element of the response. 076 */ 077 private static final byte TYPE_BASE_DNS = (byte) 0xA1; 078 079 080 081 /** 082 * The serial version UID for this serializable class. 083 */ 084 private static final long serialVersionUID = 4010094216900393866L; 085 086 087 088 // The transaction ID returned by the server. 089 private final ASN1OctetString transactionID; 090 091 // The list of base DNs returned by the server, if any. 092 private final List<String> baseDNs; 093 094 095 096 /** 097 * Creates a new start interactive transaction extended result from the 098 * provided extended result. 099 * 100 * @param extendedResult The extended result to be decoded as a start 101 * interactive transaction extended result. It must 102 * not be {@code null}. 103 * 104 * @throws LDAPException If a problem occurs while attempting to decode the 105 * provided extended result as a start interactive 106 * transaction extended result. 107 */ 108 public StartInteractiveTransactionExtendedResult( 109 final ExtendedResult extendedResult) 110 throws LDAPException 111 { 112 super(extendedResult); 113 114 if (! extendedResult.hasValue()) 115 { 116 transactionID = null; 117 baseDNs = null; 118 return; 119 } 120 121 final ASN1Sequence valueSequence; 122 try 123 { 124 final ASN1Element valueElement = 125 ASN1Element.decode(extendedResult.getValue().getValue()); 126 valueSequence = ASN1Sequence.decodeAsSequence(valueElement); 127 } 128 catch (Exception e) 129 { 130 debugException(e); 131 throw new LDAPException(ResultCode.DECODING_ERROR, 132 ERR_START_INT_TXN_RESULT_VALUE_NOT_SEQUENCE.get(e.getMessage()), e); 133 } 134 135 ASN1OctetString txnID = null; 136 List<String> baseDNList = null; 137 for (final ASN1Element element : valueSequence.elements()) 138 { 139 switch (element.getType()) 140 { 141 case TYPE_TXN_ID: 142 txnID = ASN1OctetString.decodeAsOctetString(element); 143 break; 144 case TYPE_BASE_DNS: 145 try 146 { 147 final ASN1Sequence baseDNsSequence = 148 ASN1Sequence.decodeAsSequence(element); 149 final ArrayList<String> dnList = 150 new ArrayList<String>(baseDNsSequence.elements().length); 151 for (final ASN1Element e : baseDNsSequence.elements()) 152 { 153 dnList.add(ASN1OctetString.decodeAsOctetString(e).stringValue()); 154 } 155 baseDNList = Collections.unmodifiableList(dnList); 156 } 157 catch (Exception e) 158 { 159 debugException(e); 160 throw new LDAPException(ResultCode.DECODING_ERROR, 161 ERR_START_INT_TXN_RESULT_BASE_DNS_NOT_SEQUENCE.get( 162 e.getMessage()), e); 163 } 164 break; 165 default: 166 throw new LDAPException(ResultCode.DECODING_ERROR, 167 ERR_START_INT_TXN_RESULT_INVALID_ELEMENT.get( 168 toHex(element.getType()))); 169 } 170 } 171 172 transactionID = txnID; 173 baseDNs = baseDNList; 174 175 if (transactionID == null) 176 { 177 throw new LDAPException(ResultCode.DECODING_ERROR, 178 ERR_START_INT_TXN_RESULT_NO_TXN_ID.get()); 179 } 180 } 181 182 183 184 /** 185 * Creates a new start interactive transaction extended result with the 186 * provided information. 187 * 188 * @param messageID The message ID for the LDAP message that is 189 * associated with this LDAP result. 190 * @param resultCode The result code from the response. 191 * @param diagnosticMessage The diagnostic message from the response, if 192 * available. 193 * @param matchedDN The matched DN from the response, if available. 194 * @param referralURLs The set of referral URLs from the response, if 195 * available. 196 * @param transactionID The transaction ID for this response, if 197 * available. 198 * @param baseDNs The list of base DNs for this response, if 199 * available. 200 * @param responseControls The set of controls from the response, if 201 * available. 202 */ 203 public StartInteractiveTransactionExtendedResult(final int messageID, 204 final ResultCode resultCode, final String diagnosticMessage, 205 final String matchedDN, final String[] referralURLs, 206 final ASN1OctetString transactionID, final List<String> baseDNs, 207 final Control[] responseControls) 208 { 209 super(messageID, resultCode, diagnosticMessage, matchedDN, referralURLs, 210 null, encodeValue(transactionID, baseDNs), responseControls); 211 212 this.transactionID = transactionID; 213 214 if (baseDNs == null) 215 { 216 this.baseDNs = null; 217 } 218 else 219 { 220 this.baseDNs = 221 Collections.unmodifiableList(new ArrayList<String>(baseDNs)); 222 } 223 } 224 225 226 227 /** 228 * Encodes the provided information into an ASN.1 octet string suitable for 229 * use as the value of this extended result. 230 * 231 * @param transactionID The transaction ID for this response, if available. 232 * @param baseDNs The list of base DNs for this response, if 233 * available. 234 * 235 * @return The ASN.1 octet string containing the encoded value, or 236 * {@code null} if no value should be used. 237 */ 238 private static ASN1OctetString encodeValue( 239 final ASN1OctetString transactionID, 240 final List<String> baseDNs) 241 { 242 if ((transactionID == null) && (baseDNs == null)) 243 { 244 return null; 245 } 246 247 final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(2); 248 if (transactionID != null) 249 { 250 elements.add(new ASN1OctetString(TYPE_TXN_ID, transactionID.getValue())); 251 } 252 253 if ((baseDNs != null) && (! baseDNs.isEmpty())) 254 { 255 final ArrayList<ASN1Element> baseDNElements = 256 new ArrayList<ASN1Element>(baseDNs.size()); 257 for (final String s : baseDNs) 258 { 259 baseDNElements.add(new ASN1OctetString(s)); 260 } 261 elements.add(new ASN1Sequence(TYPE_BASE_DNS, baseDNElements)); 262 } 263 264 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 265 } 266 267 268 269 /** 270 * Retrieves the transaction ID for this start interactive transaction 271 * extended result, if available. 272 * 273 * @return The transaction ID for this start interactive transaction extended 274 * result, or {@code null} if none was provided. 275 */ 276 public ASN1OctetString getTransactionID() 277 { 278 return transactionID; 279 } 280 281 282 283 /** 284 * Retrieves the list of base DNs for this start interactive transaction 285 * extended result, if available. 286 * 287 * @return The list of base DNs for this start interactive transaction 288 * extended result, or {@code null} if no base DN list was provided. 289 */ 290 public List<String> getBaseDNs() 291 { 292 return baseDNs; 293 } 294 295 296 297 /** 298 * {@inheritDoc} 299 */ 300 @Override() 301 public String getExtendedResultName() 302 { 303 return INFO_EXTENDED_RESULT_NAME_START_INTERACTIVE_TXN.get(); 304 } 305 306 307 308 /** 309 * {@inheritDoc} 310 */ 311 @Override() 312 public void toString(final StringBuilder buffer) 313 { 314 buffer.append("StartInteractiveTransactionExtendedResult(resultCode="); 315 buffer.append(getResultCode()); 316 317 final int messageID = getMessageID(); 318 if (messageID >= 0) 319 { 320 buffer.append(", messageID="); 321 buffer.append(messageID); 322 } 323 324 if (transactionID != null) 325 { 326 buffer.append(", transactionID='"); 327 buffer.append(transactionID.stringValue()); 328 buffer.append('\''); 329 } 330 331 if (baseDNs != null) 332 { 333 buffer.append(", baseDNs={"); 334 for (int i=0; i < baseDNs.size(); i++) 335 { 336 if (i > 0) 337 { 338 buffer.append(", "); 339 } 340 341 buffer.append('\''); 342 buffer.append(baseDNs.get(i)); 343 buffer.append('\''); 344 } 345 buffer.append('}'); 346 } 347 348 final String diagnosticMessage = getDiagnosticMessage(); 349 if (diagnosticMessage != null) 350 { 351 buffer.append(", diagnosticMessage='"); 352 buffer.append(diagnosticMessage); 353 buffer.append('\''); 354 } 355 356 final String matchedDN = getMatchedDN(); 357 if (matchedDN != null) 358 { 359 buffer.append(", matchedDN='"); 360 buffer.append(matchedDN); 361 buffer.append('\''); 362 } 363 364 final String[] referralURLs = getReferralURLs(); 365 if (referralURLs.length > 0) 366 { 367 buffer.append(", referralURLs={"); 368 for (int i=0; i < referralURLs.length; i++) 369 { 370 if (i > 0) 371 { 372 buffer.append(", "); 373 } 374 375 buffer.append('\''); 376 buffer.append(referralURLs[i]); 377 buffer.append('\''); 378 } 379 buffer.append('}'); 380 } 381 382 final Control[] responseControls = getResponseControls(); 383 if (responseControls.length > 0) 384 { 385 buffer.append(", responseControls={"); 386 for (int i=0; i < responseControls.length; i++) 387 { 388 if (i > 0) 389 { 390 buffer.append(", "); 391 } 392 393 buffer.append(responseControls[i]); 394 } 395 buffer.append('}'); 396 } 397 398 buffer.append(')'); 399 } 400 }