001/* 002 * Copyright 2013-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 java.util.ArrayList; 026import java.util.Collection; 027import java.util.Collections; 028import java.util.Iterator; 029import java.util.List; 030import java.util.TreeSet; 031 032import com.unboundid.asn1.ASN1Element; 033import com.unboundid.asn1.ASN1OctetString; 034import com.unboundid.asn1.ASN1Sequence; 035import com.unboundid.ldap.sdk.Control; 036import com.unboundid.ldap.sdk.ExtendedResult; 037import com.unboundid.ldap.sdk.LDAPException; 038import com.unboundid.ldap.sdk.ResultCode; 039import com.unboundid.util.Debug; 040import com.unboundid.util.StaticUtils; 041import com.unboundid.util.ThreadSafety; 042import com.unboundid.util.ThreadSafetyLevel; 043import com.unboundid.util.Validator; 044 045import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*; 046 047 048 049/** 050 * This class provides an implementation of an extended result that can be used 051 * to retrieve a list of all available versions of the configuration within a 052 * server. This may include not only the currently-active configuration, but 053 * also former configurations that have been archived, and the baseline 054 * configuration for the current server version. 055 * <BR> 056 * <BLOCKQUOTE> 057 * <B>NOTE:</B> This class, and other classes within the 058 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 059 * supported for use against Ping Identity, UnboundID, and 060 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 061 * for proprietary functionality or for external specifications that are not 062 * considered stable or mature enough to be guaranteed to work in an 063 * interoperable way with other types of LDAP servers. 064 * </BLOCKQUOTE> 065 * <BR> 066 * The OID for this extended result is 1.3.6.1.4.1.30221.2.6.27. If the request 067 * was processed successfully, then the response will have a value with the 068 * following encoding: 069 * <PRE> 070 * ListConfigurationsResult ::= SEQUENCE { 071 * activeConfigFileName [0] OCTET STRING, 072 * baselineConfigFileNames [1] OCTET STRING OPTIONAL, 073 * archivedConfigFileNames [2] SEQUENCE OF OCTET STRING OPTIONAL, 074 * ... } 075 * </PRE> 076 */ 077@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 078public final class ListConfigurationsExtendedResult 079 extends ExtendedResult 080{ 081 /** 082 * The OID (1.3.6.1.4.1.30221.2.6.27) for the list configurations extended 083 * result. 084 */ 085 public static final String LIST_CONFIGS_RESULT_OID = 086 "1.3.6.1.4.1.30221.2.6.27"; 087 088 089 090 /** 091 * The BER type for the element holding the filename used for the active 092 * configuration. 093 */ 094 private static final byte TYPE_ACTIVE_CONFIG_FILE_NAME = (byte) 0x80; 095 096 097 098 /** 099 * The BER type for the element holding the filename used for the baseline 100 * configuration. 101 */ 102 private static final byte TYPE_BASELINE_CONFIG_FILE_NAMES = (byte) 0xA1; 103 104 105 106 /** 107 * The BER type for the element holding the filenames used for the archived 108 * configurations. 109 */ 110 private static final byte TYPE_ARCHIVED_CONFIG_FILE_NAMES = (byte) 0xA2; 111 112 113 114 /** 115 * The serial version UID for this serializable class. 116 */ 117 private static final long serialVersionUID = -466738484294922561L; 118 119 120 121 // The names of the archived configuration files. 122 private final List<String> archivedFileNames; 123 124 // The name of the baseline configuration file. 125 private final List<String> baselineFileNames; 126 127 // The name of the active configuration file. 128 private final String activeFileName; 129 130 131 132 /** 133 * Creates a new list configurations extended result from the provided generic 134 * extended result. 135 * 136 * @param result The generic extended result to be decoded as a list 137 * configurations extended result. 138 * 139 * @throws LDAPException If the provided extended result cannot be parsed as 140 * a valid list configurations extended result. 141 */ 142 public ListConfigurationsExtendedResult(final ExtendedResult result) 143 throws LDAPException 144 { 145 super(result); 146 147 final ASN1OctetString value = result.getValue(); 148 if (value == null) 149 { 150 activeFileName = null; 151 baselineFileNames = Collections.emptyList(); 152 archivedFileNames = Collections.emptyList(); 153 return; 154 } 155 156 try 157 { 158 String activeName = null; 159 List<String> archivedNames = Collections.emptyList(); 160 List<String> baselineNames = null; 161 final ASN1Element[] elements = 162 ASN1Sequence.decodeAsSequence(value.getValue()).elements(); 163 for (final ASN1Element e : elements) 164 { 165 switch (e.getType()) 166 { 167 case TYPE_ACTIVE_CONFIG_FILE_NAME: 168 activeName = ASN1OctetString.decodeAsOctetString(e).stringValue(); 169 break; 170 case TYPE_BASELINE_CONFIG_FILE_NAMES: 171 final ASN1Element[] baselineNameElements = 172 ASN1Sequence.decodeAsSequence(e).elements(); 173 baselineNames = new ArrayList<>(baselineNameElements.length); 174 for (final ASN1Element el : baselineNameElements) 175 { 176 baselineNames.add( 177 ASN1OctetString.decodeAsOctetString(el).stringValue()); 178 } 179 archivedNames = Collections.unmodifiableList(baselineNames); 180 break; 181 case TYPE_ARCHIVED_CONFIG_FILE_NAMES: 182 final ASN1Element[] archivedNameElements = 183 ASN1Sequence.decodeAsSequence(e).elements(); 184 archivedNames = new ArrayList<>(archivedNameElements.length); 185 for (final ASN1Element el : archivedNameElements) 186 { 187 archivedNames.add( 188 ASN1OctetString.decodeAsOctetString(el).stringValue()); 189 } 190 archivedNames = Collections.unmodifiableList(archivedNames); 191 break; 192 default: 193 throw new LDAPException(ResultCode.DECODING_ERROR, 194 ERR_LIST_CONFIGS_RESULT_UNEXPECTED_ELEMENT_TYPE.get( 195 StaticUtils.toHex(e.getType()))); 196 } 197 } 198 199 activeFileName = activeName; 200 archivedFileNames = archivedNames; 201 baselineFileNames = baselineNames; 202 203 if (activeFileName == null) 204 { 205 throw new LDAPException(ResultCode.DECODING_ERROR, 206 ERR_LIST_CONFIGS_RESULT_NO_ACTIVE_CONFIG.get()); 207 } 208 } 209 catch (final LDAPException le) 210 { 211 Debug.debugException(le); 212 throw le; 213 } 214 catch (final Exception e) 215 { 216 Debug.debugException(e); 217 throw new LDAPException(ResultCode.DECODING_ERROR, 218 ERR_LIST_CONFIGS_RESULT_ERROR_PARSING_VALUE.get( 219 StaticUtils.getExceptionMessage(e)), 220 e); 221 } 222 } 223 224 225 226 /** 227 * Creates a new list configurations extended result with the provided 228 * information. 229 * 230 * @param messageID The message ID for the LDAP message that is 231 * associated with this LDAP result. 232 * @param resultCode The result code from the response. 233 * @param diagnosticMessage The diagnostic message from the response, if 234 * available. 235 * @param matchedDN The matched DN from the response, if available. 236 * @param referralURLs The set of referral URLs from the response, if 237 * available. 238 * @param activeFileName The name of the active configuration file, if 239 * available. 240 * @param baselineFileNames The names of the baseline configuration files 241 * for current and former server versions, if 242 * available. It must be {@code null} or empty if 243 * the active file name is {@code null}. 244 * @param archivedFileNames The names of the archived configuration files, 245 * if available. It must be {@code null} or empty 246 * if the active file name is {@code null}. 247 * @param responseControls The set of controls from the response, if 248 * available. 249 */ 250 public ListConfigurationsExtendedResult(final int messageID, 251 final ResultCode resultCode, final String diagnosticMessage, 252 final String matchedDN, final String[] referralURLs, 253 final String activeFileName, 254 final Collection<String> baselineFileNames, 255 final Collection<String> archivedFileNames, 256 final Control... responseControls) 257 { 258 super(messageID, resultCode, diagnosticMessage, matchedDN, referralURLs, 259 ((activeFileName == null) ? null : LIST_CONFIGS_RESULT_OID), 260 encodeValue(activeFileName, baselineFileNames, archivedFileNames), 261 responseControls); 262 263 this.activeFileName = activeFileName; 264 265 if (baselineFileNames == null) 266 { 267 this.baselineFileNames = Collections.emptyList(); 268 } 269 else 270 { 271 this.baselineFileNames = 272 Collections.unmodifiableList(new ArrayList<>(baselineFileNames)); 273 } 274 275 if (archivedFileNames == null) 276 { 277 this.archivedFileNames = Collections.emptyList(); 278 } 279 else 280 { 281 this.archivedFileNames = 282 Collections.unmodifiableList(new ArrayList<>(archivedFileNames)); 283 } 284 } 285 286 287 288 /** 289 * Creates an ASN.1 octet string containing an encoded representation of the 290 * value for a list configurations extended result with the provided 291 * information. 292 * 293 * @param activeFileName The name of the active configuration file, if 294 * available. 295 * @param baselineFileNames The names of the baseline configuration files 296 * for current and former server versions, if 297 * available. It must be {@code null} or empty if 298 * the active file name is {@code null}. 299 * @param archivedFileNames The names of the archived configuration files, 300 * if available. It must be {@code null} or empty 301 * if the active file name is {@code null}. 302 * 303 * @return An ASN.1 octet string containing an encoded representation of the 304 * value for a list configurations extended result, or {@code null} 305 * if a result with the provided information should not have a value. 306 */ 307 public static ASN1OctetString encodeValue(final String activeFileName, 308 final Collection<String> baselineFileNames, 309 final Collection<String> archivedFileNames) 310 { 311 if (activeFileName == null) 312 { 313 Validator.ensureTrue( 314 ((baselineFileNames == null) || baselineFileNames.isEmpty()), 315 "The baseline filename must be null if the active filename is null"); 316 Validator.ensureTrue( 317 ((archivedFileNames == null) || archivedFileNames.isEmpty()), 318 "The archived filenames must be null or empty if the active " + 319 "filename is null"); 320 return null; 321 } 322 323 final ArrayList<ASN1Element> elements = new ArrayList<>(3); 324 elements.add( 325 new ASN1OctetString(TYPE_ACTIVE_CONFIG_FILE_NAME, activeFileName)); 326 327 if ((baselineFileNames != null) && (! baselineFileNames.isEmpty())) 328 { 329 final TreeSet<String> sortedBaselineNames = 330 new TreeSet<>(baselineFileNames); 331 final ArrayList<ASN1Element> baselineNameElements = 332 new ArrayList<>(sortedBaselineNames.size()); 333 for (final String s : sortedBaselineNames) 334 { 335 baselineNameElements.add(new ASN1OctetString(s)); 336 } 337 elements.add(new ASN1Sequence(TYPE_BASELINE_CONFIG_FILE_NAMES, 338 baselineNameElements)); 339 } 340 341 if ((archivedFileNames != null) && (! archivedFileNames.isEmpty())) 342 { 343 final TreeSet<String> sortedArchivedNames = 344 new TreeSet<>(archivedFileNames); 345 final ArrayList<ASN1Element> archivedNameElements = 346 new ArrayList<>(sortedArchivedNames.size()); 347 for (final String s : sortedArchivedNames) 348 { 349 archivedNameElements.add(new ASN1OctetString(s)); 350 } 351 elements.add(new ASN1Sequence(TYPE_ARCHIVED_CONFIG_FILE_NAMES, 352 archivedNameElements)); 353 } 354 355 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 356 } 357 358 359 360 /** 361 * Retrieves the name of the active configuration file the server is 362 * currently using, if available. 363 * 364 * @return The name of the active configuration file the server is 365 * currently using, or {@code null} this is not available. 366 */ 367 public String getActiveFileName() 368 { 369 return activeFileName; 370 } 371 372 373 374 /** 375 * Retrieves a list containing the names of the baseline configuration files 376 * (i.e., the files containing the initial "out-of-the-box" configuration for 377 * various server versions), if available. 378 * 379 * @return A list containing the names of the baseline configuration files, 380 * or an empty list if this is not available. 381 */ 382 public List<String> getBaselineFileNames() 383 { 384 return baselineFileNames; 385 } 386 387 388 389 /** 390 * Retrieves a list containing the names of the archived configuration files, 391 * if available. 392 * 393 * @return A list containing the names of the archived configuration files, 394 * or an empty list if this is not available. 395 */ 396 public List<String> getArchivedFileNames() 397 { 398 return archivedFileNames; 399 } 400 401 402 403 /** 404 * {@inheritDoc} 405 */ 406 @Override() 407 public String getExtendedResultName() 408 { 409 return INFO_EXTENDED_RESULT_NAME_LIST_CONFIGS.get(); 410 } 411 412 413 414 /** 415 * {@inheritDoc} 416 */ 417 @Override() 418 public void toString(final StringBuilder buffer) 419 { 420 buffer.append("ListConfigurationsExtendedResult(resultCode="); 421 buffer.append(getResultCode()); 422 423 final int messageID = getMessageID(); 424 if (messageID >= 0) 425 { 426 buffer.append(", messageID="); 427 buffer.append(messageID); 428 } 429 430 if (activeFileName != null) 431 { 432 buffer.append(", activeFileName='"); 433 buffer.append(activeFileName); 434 buffer.append('\''); 435 } 436 437 if (! baselineFileNames.isEmpty()) 438 { 439 buffer.append(", baselineFileNames={"); 440 441 final Iterator<String> iterator = baselineFileNames.iterator(); 442 while (iterator.hasNext()) 443 { 444 buffer.append('\''); 445 buffer.append(iterator.next()); 446 buffer.append('\''); 447 if (iterator.hasNext()) 448 { 449 buffer.append(','); 450 } 451 } 452 453 buffer.append('}'); 454 } 455 456 if (! archivedFileNames.isEmpty()) 457 { 458 buffer.append(", archivedFileNames={"); 459 460 final Iterator<String> iterator = archivedFileNames.iterator(); 461 while (iterator.hasNext()) 462 { 463 buffer.append('\''); 464 buffer.append(iterator.next()); 465 buffer.append('\''); 466 if (iterator.hasNext()) 467 { 468 buffer.append(','); 469 } 470 } 471 472 buffer.append('}'); 473 } 474 475 final String diagnosticMessage = getDiagnosticMessage(); 476 if (diagnosticMessage != null) 477 { 478 buffer.append(", diagnosticMessage='"); 479 buffer.append(diagnosticMessage); 480 buffer.append('\''); 481 } 482 483 final String matchedDN = getMatchedDN(); 484 if (matchedDN != null) 485 { 486 buffer.append(", matchedDN='"); 487 buffer.append(matchedDN); 488 buffer.append('\''); 489 } 490 491 final String[] referralURLs = getReferralURLs(); 492 if (referralURLs.length > 0) 493 { 494 buffer.append(", referralURLs={"); 495 for (int i=0; i < referralURLs.length; i++) 496 { 497 if (i > 0) 498 { 499 buffer.append(", "); 500 } 501 502 buffer.append('\''); 503 buffer.append(referralURLs[i]); 504 buffer.append('\''); 505 } 506 buffer.append('}'); 507 } 508 509 final Control[] responseControls = getResponseControls(); 510 if (responseControls.length > 0) 511 { 512 buffer.append(", responseControls={"); 513 for (int i=0; i < responseControls.length; i++) 514 { 515 if (i > 0) 516 { 517 buffer.append(", "); 518 } 519 520 buffer.append(responseControls[i]); 521 } 522 buffer.append('}'); 523 } 524 525 buffer.append(')'); 526 } 527}