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