001/* 002 * Copyright 2020 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 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) 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.tasks; 037 038 039 040import java.util.ArrayList; 041import java.util.Arrays; 042import java.util.Collections; 043import java.util.LinkedHashMap; 044import java.util.List; 045import java.util.Map; 046import java.util.concurrent.TimeUnit; 047 048import com.unboundid.ldap.sdk.Attribute; 049import com.unboundid.ldap.sdk.Entry; 050import com.unboundid.util.Debug; 051import com.unboundid.util.NotMutable; 052import com.unboundid.util.StaticUtils; 053import com.unboundid.util.ThreadSafety; 054import com.unboundid.util.ThreadSafetyLevel; 055import com.unboundid.util.args.ArgumentException; 056import com.unboundid.util.args.DurationArgument; 057 058import static com.unboundid.ldap.sdk.unboundidds.tasks.TaskMessages.*; 059 060 061 062/** 063 * This class defines a Directory Server task that can be used to invoke the 064 * collect-support-data tool to capture a variety of information that may help 065 * monitor the state of the server or diagnose potential problems. 066 * <BR> 067 * <BLOCKQUOTE> 068 * <B>NOTE:</B> This class, and other classes within the 069 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 070 * supported for use against Ping Identity, UnboundID, and 071 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 072 * for proprietary functionality or for external specifications that are not 073 * considered stable or mature enough to be guaranteed to work in an 074 * interoperable way with other types of LDAP servers. 075 * </BLOCKQUOTE> 076 * <BR> 077 * The properties that are available for use with this type of task include: 078 * <UL> 079 * <LI>The path (on the server filesystem) to which the support data archive 080 * should be written. If this is not provided, then the server will 081 * determine an appropriate output file to use. If this is provided and 082 * refers to a file that exists, that file will be overwritten. If this 083 * is provided and refers to a directory that exists, then a file will 084 * be created in that directory with a server-generated name. If this 085 * is provided and refers to a file that does not exist, then its parent 086 * directory must exist, and a new file will be created with the specified 087 * path.</LI> 088 * <LI>The path (on the server filesystem) to a file containing the passphrase 089 * to use to encrypt the contents of the support data archive. If this is 090 * not provided, then the support data archive will not be encrypted.</LI> 091 * <LI>A flag that indicates whether to include data that may be expensive to 092 * capture in the support data archive. This information will not be 093 * included by default.</LI> 094 * <LI>A flag that indicates whether to include a replication state dump 095 * (which may be several megabytes in size) in the support data 096 * archive. This information will not be included by default.</LI> 097 * <LI>A flag that indicates whether to include binary files in the support 098 * data archive. Binary files will not be included by default.</LI> 099 * <LI>A flag that indicates whether to include source code (if available) to 100 * any third-party extensions installed in the server. Extension source 101 * code will not be included by default.</LI> 102 * <LI>The data security level to use when redacting data to include in the 103 * support data archive. If this is not specified, the server will 104 * select an appropriate security level.</LI> 105 * <LI>A flag that indicates whether to capture items in sequential mode 106 * (which will use less memory, but at the expense of taking longer to 107 * complete) rather than in parallel. Support data will be captured in 108 * parallel by default.</LI> 109 * <LI>The number and duration between intervals for use when collecting 110 * output of tools (like vmstat, iostat, mpstat, etc.) that use 111 * sampling over time. If this is not provided, the server will use a 112 * default count and interval.</LI> 113 * <LI>The number of times to invoke the jstack utility to obtain a stack 114 * trace of threads running in the JVM. If this is not provided, the 115 * server will use a default count.</LI> 116 * <LI>The duration (the length of time before the time the task is invoked) 117 * for log messages to be included in the support data archive. If this 118 * is not provided, the server will automatically select the amount of 119 * log content to include.</LI> 120 * <LI>An optional comment to include in the support data archive.</LI> 121 * </UL> 122 */ 123@NotMutable() 124@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 125public final class CollectSupportDataTask 126 extends Task 127{ 128 /** 129 * The fully-qualified name of the Java class that is used for the collect 130 * support data task. 131 */ 132 static final String COLLECT_SUPPORT_DATA_TASK_CLASS = 133 "com.unboundid.directory.server.tasks.CollectSupportDataTask"; 134 135 136 137 /** 138 * The prefix that will appear at the beginning of all attribute names used by 139 * the collect support data task. 140 */ 141 private static final String ATTR_PREFIX = "ds-task-collect-support-data-"; 142 143 144 145 /** 146 * The name of the attribute used to specify a comment to include in the 147 * support data archive. 148 */ 149 public static final String ATTR_COMMENT = ATTR_PREFIX + "comment"; 150 151 152 153 /** 154 * The name of the attribute used to specify the path to a file containing the 155 * passphrase to use to encrypt the contents of the support data archive. 156 */ 157 public static final String ATTR_ENCRYPTION_PASSPHRASE_FILE = 158 ATTR_PREFIX + "encryption-passphrase-file"; 159 160 161 162 /** 163 * The name of the attribute used to indicate whether the support data archive 164 * may include binary files that may otherwise have been omitted. 165 */ 166 public static final String ATTR_INCLUDE_BINARY_FILES = 167 ATTR_PREFIX + "include-binary-files"; 168 169 170 171 /** 172 * The name of the attribute used to indicate whether the support data archive 173 * should include information that may be expensive to capture. 174 */ 175 public static final String ATTR_INCLUDE_EXPENSIVE_DATA = 176 ATTR_PREFIX + "include-expensive-data"; 177 178 179 180 /** 181 * The name of the attribute used to indicate whether the support data archive 182 * may include the source code (if available) for any third-party extensions 183 * installed in the server. 184 */ 185 public static final String ATTR_INCLUDE_EXTENSION_SOURCE = 186 ATTR_PREFIX + "include-extension-source"; 187 188 189 190 /** 191 * The name of the attribute used to indicate whether the support data archive 192 * should include a replication state dump (which may be several megabytes in 193 * size). 194 */ 195 public static final String ATTR_INCLUDE_REPLICATION_STATE_DUMP = 196 ATTR_PREFIX + "include-replication-state-dump"; 197 198 199 200 /** 201 * The name of the attribute used to specify the number of times to invoke the 202 * jstack utility to capture server thread stack traces. 203 */ 204 public static final String ATTR_JSTACK_COUNT = ATTR_PREFIX + "jstack-count"; 205 206 207 208 /** 209 * The name of the attribute used to specify the length of time that should be 210 * covered by the log data included in the support data archive. 211 */ 212 public static final String ATTR_LOG_DURATION = ATTR_PREFIX + "log-duration"; 213 214 215 216 /** 217 * The name of the attribute used to specify the path to which the support 218 * data archive should be written. 219 */ 220 public static final String ATTR_OUTPUT_PATH = ATTR_PREFIX + "output-path"; 221 222 223 224 /** 225 * The name of the attribute used to specify the number of intervals to 226 * capture for tools that capture multiple samples. 227 */ 228 public static final String ATTR_REPORT_COUNT = ATTR_PREFIX + "report-count"; 229 230 231 232 /** 233 * The name of the attribute used to specify the length of time, in seconds, 234 * between samples collected from tools that capture multiple samples. 235 */ 236 public static final String ATTR_REPORT_INTERVAL_SECONDS = 237 ATTR_PREFIX + "report-interval-seconds"; 238 239 240 241 /** 242 *The name of the attribute used to specify the minimum age of previous 243 * support data archives that should be retained. 244 */ 245 public static final String ATTR_RETAIN_PREVIOUS_ARCHIVE_AGE = 246 ATTR_PREFIX + "retain-previous-support-data-archive-age"; 247 248 249 250 /** 251 *The name of the attribute used to specify the minimum number of previous 252 * support data archives that should be retained. 253 */ 254 public static final String ATTR_RETAIN_PREVIOUS_ARCHIVE_COUNT = 255 ATTR_PREFIX + "retain-previous-support-data-archive-count"; 256 257 258 259 /** 260 * The name of the attribute used to specify the security level to use for 261 * information added to the support data archive. 262 */ 263 public static final String ATTR_SECURITY_LEVEL = 264 ATTR_PREFIX + "security-level"; 265 266 267 268 /** 269 * The name of the attribute used to indicate whether to collect items 270 * sequentially rather than in parallel. 271 */ 272 public static final String ATTR_USE_SEQUENTIAL_MODE = 273 ATTR_PREFIX + "use-sequential-mode"; 274 275 276 277 /** 278 * The name of the object class used in collect support data task entries. 279 */ 280 public static final String OC_COLLECT_SUPPORT_DATA_TASK = 281 "ds-task-collect-support-data"; 282 283 284 285 /** 286 * The task property that will be used for the comment. 287 */ 288 static final TaskProperty PROPERTY_COMMENT = 289 new TaskProperty(ATTR_COMMENT, INFO_CSD_DISPLAY_NAME_COMMENT.get(), 290 INFO_CSD_DESCRIPTION_COMMENT.get(), String.class, false, false, 291 false); 292 293 294 295 /** 296 * The task property that will be used for the encryption passphrase file. 297 */ 298 static final TaskProperty PROPERTY_ENCRYPTION_PASSPHRASE_FILE = 299 new TaskProperty(ATTR_ENCRYPTION_PASSPHRASE_FILE, 300 INFO_CSD_DISPLAY_NAME_ENC_PW_FILE.get(), 301 INFO_CSD_DESCRIPTION_ENC_PW_FILE.get(), String.class, false, false, 302 false); 303 304 305 306 /** 307 * The task property that will be used for the include binary files flag. 308 */ 309 static final TaskProperty PROPERTY_INCLUDE_BINARY_FILES = 310 new TaskProperty(ATTR_INCLUDE_BINARY_FILES, 311 INFO_CSD_DISPLAY_NAME_INCLUDE_BINARY_FILES.get(), 312 INFO_CSD_DESCRIPTION_INCLUDE_BINARY_FILES.get(), Boolean.class, false, 313 false, false); 314 315 316 317 /** 318 * The task property that will be used for the include expensive data flag. 319 */ 320 static final TaskProperty PROPERTY_INCLUDE_EXPENSIVE_DATA = 321 new TaskProperty(ATTR_INCLUDE_EXPENSIVE_DATA, 322 INFO_CSD_DISPLAY_NAME_INCLUDE_EXPENSIVE_DATA.get(), 323 INFO_CSD_DESCRIPTION_INCLUDE_EXPENSIVE_DATA.get(), Boolean.class, 324 false, false, false); 325 326 327 328 /** 329 * The task property that will be used for the include extension source flag. 330 */ 331 static final TaskProperty PROPERTY_INCLUDE_EXTENSION_SOURCE = 332 new TaskProperty(ATTR_INCLUDE_EXTENSION_SOURCE, 333 INFO_CSD_DISPLAY_NAME_INCLUDE_EXTENSION_SOURCE.get(), 334 INFO_CSD_DESCRIPTION_INCLUDE_EXTENSION_SOURCE.get(), Boolean.class, 335 false, false, false); 336 337 338 339 /** 340 * The task property that will be used for the include replication state dump 341 * flag. 342 */ 343 static final TaskProperty PROPERTY_INCLUDE_REPLICATION_STATE_DUMP = 344 new TaskProperty(ATTR_INCLUDE_REPLICATION_STATE_DUMP, 345 INFO_CSD_DISPLAY_NAME_INCLUDE_REPLICATION_STATE_DUMP.get(), 346 INFO_CSD_DESCRIPTION_INCLUDE_REPLICATION_STATE_DUMP.get(), 347 Boolean.class, false, false, false); 348 349 350 351 /** 352 * The task property that will be used for the jstack count. 353 */ 354 static final TaskProperty PROPERTY_JSTACK_COUNT = 355 new TaskProperty(ATTR_JSTACK_COUNT, 356 INFO_CSD_DISPLAY_NAME_JSTACK_COUNT.get(), 357 INFO_CSD_DESCRIPTION_JSTACK_COUNT.get(), 358 Long.class, false, false, false); 359 360 361 362 /** 363 * The task property that will be used for the log duration. 364 */ 365 static final TaskProperty PROPERTY_LOG_DURATION = 366 new TaskProperty(ATTR_LOG_DURATION, 367 INFO_CSD_DISPLAY_NAME_LOG_DURATION.get(), 368 INFO_CSD_DESCRIPTION_LOG_DURATION.get(), 369 String.class, false, false, false); 370 371 372 373 /** 374 * The task property that will be used for the output path. 375 */ 376 static final TaskProperty PROPERTY_OUTPUT_PATH = 377 new TaskProperty(ATTR_OUTPUT_PATH, 378 INFO_CSD_DISPLAY_NAME_OUTPUT_PATH.get(), 379 INFO_CSD_DESCRIPTION_OUTPUT_PATH.get(), 380 String.class, false, false, false); 381 382 383 384 /** 385 * The task property that will be used for the report count. 386 */ 387 static final TaskProperty PROPERTY_REPORT_COUNT = 388 new TaskProperty(ATTR_REPORT_COUNT, 389 INFO_CSD_DISPLAY_NAME_REPORT_COUNT.get(), 390 INFO_CSD_DESCRIPTION_REPORT_COUNT.get(), 391 Long.class, false, false, false); 392 393 394 395 /** 396 * The task property that will be used for the report interval. 397 */ 398 static final TaskProperty PROPERTY_REPORT_INTERVAL_SECONDS = 399 new TaskProperty(ATTR_REPORT_INTERVAL_SECONDS, 400 INFO_CSD_DISPLAY_NAME_REPORT_INTERVAL.get(), 401 INFO_CSD_DESCRIPTION_REPORT_INTERVAL.get(), 402 Long.class, false, false, false); 403 404 405 406 /** 407 * The task property that will be used for the retain previous support data 408 * archive age. 409 */ 410 static final TaskProperty PROPERTY_RETAIN_PREVIOUS_ARCHIVE_AGE = 411 new TaskProperty(ATTR_RETAIN_PREVIOUS_ARCHIVE_AGE, 412 INFO_CSD_DISPLAY_NAME_RETAIN_PREVIOUS_ARCHIVE_AGE.get(), 413 INFO_CSD_DESCRIPTION_RETAIN_PREVIOUS_ARCHIVE_AGE.get(), 414 String.class, false, false, false); 415 416 417 418 /** 419 * The task property that will be used for the retain previous support data 420 * archive count. 421 */ 422 static final TaskProperty PROPERTY_RETAIN_PREVIOUS_ARCHIVE_COUNT = 423 new TaskProperty(ATTR_RETAIN_PREVIOUS_ARCHIVE_COUNT, 424 INFO_CSD_DISPLAY_NAME_RETAIN_PREVIOUS_ARCHIVE_COUNT.get(), 425 INFO_CSD_DESCRIPTION_RETAIN_PREVIOUS_ARCHIVE_COUNT.get(), 426 Long.class, false, false, false); 427 428 429 430 /** 431 * The task property that will be used for the security level. 432 */ 433 static final TaskProperty PROPERTY_SECURITY_LEVEL = 434 new TaskProperty(ATTR_SECURITY_LEVEL, 435 INFO_CSD_DISPLAY_NAME_SECURITY_LEVEL.get(), 436 INFO_CSD_DESCRIPTION_SECURITY_LEVEL.get( 437 CollectSupportDataSecurityLevel.NONE.getName(), 438 CollectSupportDataSecurityLevel.OBSCURE_SECRETS.getName(), 439 CollectSupportDataSecurityLevel.MAXIMUM.getName()), 440 String.class, false, false, false, 441 new Object[] 442 { 443 CollectSupportDataSecurityLevel.NONE.getName(), 444 CollectSupportDataSecurityLevel.OBSCURE_SECRETS.getName(), 445 CollectSupportDataSecurityLevel.MAXIMUM.getName() 446 }); 447 448 449 450 /** 451 * The task property that will be used for the use sequential mode flag. 452 */ 453 static final TaskProperty PROPERTY_USE_SEQUENTIAL_MODE = 454 new TaskProperty(ATTR_USE_SEQUENTIAL_MODE, 455 INFO_CSD_DISPLAY_NAME_USE_SEQUENTIAL_MODE.get(), 456 INFO_CSD_DESCRIPTION_USE_SEQUENTIAL_MODE.get(), 457 Boolean.class, false, false, false); 458 459 460 461 /** 462 * The serial version UID for this serializable class. 463 */ 464 private static final long serialVersionUID = -2568906018686907596L; 465 466 467 468 // Indicates whether to include binary files in the support data archive. 469 private final Boolean includeBinaryFiles; 470 471 // Indicates whether to include expensive data in the support data archive. 472 private final Boolean includeExpensiveData; 473 474 // Indicates whether to include third-party extension source code in the 475 // support data archive. 476 private final Boolean includeExtensionSource; 477 478 // Indicates whether to include a replication state dump in the support data 479 // archive. 480 private final Boolean includeReplicationStateDump; 481 482 // Indicates whether to capture information sequentially rather than in 483 // parallel. 484 private final Boolean useSequentialMode; 485 486 // The security level to use for data included in the support data archive. 487 private final CollectSupportDataSecurityLevel securityLevel; 488 489 // The number of jstacks to include in the support data archive. 490 private final Integer jstackCount; 491 492 // The report count to use for sampled metrics. 493 private final Integer reportCount; 494 495 // The report interval, in seconds, to use for sampled metrics. 496 private final Integer reportIntervalSeconds; 497 498 // The minimum number of existing support data archives that should be 499 // retained. 500 private final Integer retainPreviousSupportDataArchiveCount; 501 502 // A comment to include in the support data archive. 503 private final String comment; 504 505 // The path to the encryption passphrase file. 506 private final String encryptionPassphraseFile; 507 508 // A string representation of the log duration to capture. 509 private final String logDuration; 510 511 // The path to which the support data archive should be written. 512 private final String outputPath; 513 514 // The minimum age for existing support data archives that should be retained. 515 private final String retainPreviousSupportDataArchiveAge; 516 517 518 519 /** 520 * Creates a new collect support data task instance that will use default 521 * settings for all properties. This instance may be used to invoke the 522 * task, but it can also be used for obtaining general information about this 523 * task, including the task name, description, and supported properties. 524 */ 525 public CollectSupportDataTask() 526 { 527 this(new CollectSupportDataTaskProperties()); 528 } 529 530 531 532 /** 533 * Creates a new collect support data task instance using the provided 534 * properties. 535 * 536 * @param properties The properties to use to create the collect support 537 * data task. It must not be {@code null}. 538 */ 539 public CollectSupportDataTask( 540 final CollectSupportDataTaskProperties properties) 541 { 542 super(properties.getTaskID(), COLLECT_SUPPORT_DATA_TASK_CLASS, 543 properties.getScheduledStartTime(), properties.getDependencyIDs(), 544 properties.getFailedDependencyAction(), properties.getNotifyOnStart(), 545 properties.getNotifyOnCompletion(), properties.getNotifyOnSuccess(), 546 properties.getNotifyOnError(), properties.getAlertOnStart(), 547 properties.getAlertOnSuccess(), properties.getAlertOnError()); 548 549 includeBinaryFiles = properties.getIncludeBinaryFiles(); 550 includeExpensiveData = properties.getIncludeExpensiveData(); 551 includeExtensionSource = properties.getIncludeExtensionSource(); 552 includeReplicationStateDump = properties.getIncludeReplicationStateDump(); 553 useSequentialMode = properties.getUseSequentialMode(); 554 securityLevel = properties.getSecurityLevel(); 555 jstackCount = properties.getJStackCount(); 556 reportCount = properties.getReportCount(); 557 reportIntervalSeconds = properties.getReportIntervalSeconds(); 558 retainPreviousSupportDataArchiveCount = 559 properties.getRetainPreviousSupportDataArchiveCount(); 560 comment = properties.getComment(); 561 encryptionPassphraseFile = properties.getEncryptionPassphraseFile(); 562 logDuration = properties.getLogDuration(); 563 outputPath = properties.getOutputPath(); 564 retainPreviousSupportDataArchiveAge = 565 properties.getRetainPreviousSupportDataArchiveAge(); 566 } 567 568 569 570 /** 571 * Creates a new collect support data task from the provided entry. 572 * 573 * @param entry The entry to use to create this collect support data task. 574 * 575 * @throws TaskException If the provided entry cannot be parsed as a collect 576 * support data task entry. 577 */ 578 public CollectSupportDataTask(final Entry entry) 579 throws TaskException 580 { 581 super(entry); 582 583 includeBinaryFiles = 584 entry.getAttributeValueAsBoolean(ATTR_INCLUDE_BINARY_FILES); 585 includeExpensiveData = 586 entry.getAttributeValueAsBoolean(ATTR_INCLUDE_EXPENSIVE_DATA); 587 includeExtensionSource = 588 entry.getAttributeValueAsBoolean(ATTR_INCLUDE_EXTENSION_SOURCE); 589 includeReplicationStateDump = 590 entry.getAttributeValueAsBoolean(ATTR_INCLUDE_REPLICATION_STATE_DUMP); 591 useSequentialMode = 592 entry.getAttributeValueAsBoolean(ATTR_USE_SEQUENTIAL_MODE); 593 594 jstackCount = entry.getAttributeValueAsInteger(ATTR_JSTACK_COUNT); 595 reportCount = entry.getAttributeValueAsInteger(ATTR_REPORT_COUNT); 596 reportIntervalSeconds = 597 entry.getAttributeValueAsInteger(ATTR_REPORT_INTERVAL_SECONDS); 598 retainPreviousSupportDataArchiveCount = 599 entry.getAttributeValueAsInteger(ATTR_RETAIN_PREVIOUS_ARCHIVE_COUNT); 600 601 comment = entry.getAttributeValue(ATTR_COMMENT); 602 encryptionPassphraseFile = 603 entry.getAttributeValue(ATTR_ENCRYPTION_PASSPHRASE_FILE); 604 outputPath = entry.getAttributeValue(ATTR_OUTPUT_PATH); 605 606 final String securityLevelStr = 607 entry.getAttributeValue(ATTR_SECURITY_LEVEL); 608 if (securityLevelStr == null) 609 { 610 securityLevel = null; 611 } 612 else 613 { 614 securityLevel = CollectSupportDataSecurityLevel.forName(securityLevelStr); 615 if (securityLevel == null) 616 { 617 throw new TaskException(ERR_CSD_ENTRY_INVALID_SECURITY_LEVEL.get( 618 entry.getDN(), securityLevelStr, ATTR_SECURITY_LEVEL, 619 CollectSupportDataSecurityLevel.NONE.getName(), 620 CollectSupportDataSecurityLevel.OBSCURE_SECRETS.getName(), 621 CollectSupportDataSecurityLevel.MAXIMUM.getName())); 622 } 623 } 624 625 logDuration = entry.getAttributeValue(ATTR_LOG_DURATION); 626 if (logDuration != null) 627 { 628 try 629 { 630 DurationArgument.parseDuration(logDuration, TimeUnit.MILLISECONDS); 631 } 632 catch (final Exception e) 633 { 634 Debug.debugException(e); 635 throw new TaskException( 636 ERR_CSD_ENTRY_INVALID_DURATION.get(entry.getDN(), logDuration, 637 ATTR_LOG_DURATION), 638 e); 639 } 640 } 641 642 retainPreviousSupportDataArchiveAge = 643 entry.getAttributeValue(ATTR_RETAIN_PREVIOUS_ARCHIVE_AGE); 644 if (retainPreviousSupportDataArchiveAge != null) 645 { 646 try 647 { 648 DurationArgument.parseDuration(retainPreviousSupportDataArchiveAge, 649 TimeUnit.MILLISECONDS); 650 } 651 catch (final Exception e) 652 { 653 Debug.debugException(e); 654 throw new TaskException( 655 ERR_CSD_ENTRY_INVALID_DURATION.get(entry.getDN(), 656 retainPreviousSupportDataArchiveAge, 657 ATTR_RETAIN_PREVIOUS_ARCHIVE_AGE), 658 e); 659 } 660 } 661 } 662 663 664 665 /** 666 * Creates a new collect support data task from the provided set of task 667 * properties. 668 * 669 * @param properties The set of task properties and their corresponding 670 * values to use for the task. It must not be 671 * {@code null}. 672 * 673 * @throws TaskException If the provided set of properties cannot be used to 674 * create a valid collect support data task. 675 */ 676 public CollectSupportDataTask(final Map<TaskProperty,List<Object>> properties) 677 throws TaskException 678 { 679 super(COLLECT_SUPPORT_DATA_TASK_CLASS, properties); 680 681 Boolean includeBinary = null; 682 Boolean includeExpensive = null; 683 Boolean includeReplicationState = null; 684 Boolean includeSource = null; 685 Boolean sequentialMode = null; 686 CollectSupportDataSecurityLevel secLevel = null; 687 Integer numJStacks = null; 688 Integer numReports = null; 689 Integer reportIntervalSecs = null; 690 Integer retainCount = null; 691 String commentStr = null; 692 String encPWFile = null; 693 String logDurationStr = null; 694 String outputPathStr = null; 695 String retainAge = null; 696 697 for (final Map.Entry<TaskProperty,List<Object>> entry : 698 properties.entrySet()) 699 { 700 final TaskProperty p = entry.getKey(); 701 final String attrName = StaticUtils.toLowerCase(p.getAttributeName()); 702 final List<Object> values = entry.getValue(); 703 704 if (attrName.equals(ATTR_INCLUDE_BINARY_FILES)) 705 { 706 includeBinary = parseBoolean(p, values, includeBinary); 707 } 708 else if (attrName.equals(ATTR_INCLUDE_EXPENSIVE_DATA)) 709 { 710 includeExpensive = parseBoolean(p, values, includeExpensive); 711 } 712 else if (attrName.equals(ATTR_INCLUDE_REPLICATION_STATE_DUMP)) 713 { 714 includeReplicationState = 715 parseBoolean(p, values, includeReplicationState); 716 } 717 else if (attrName.equals(ATTR_INCLUDE_EXTENSION_SOURCE)) 718 { 719 includeSource = parseBoolean(p, values, includeSource); 720 } 721 else if (attrName.equals(ATTR_USE_SEQUENTIAL_MODE)) 722 { 723 sequentialMode = parseBoolean(p, values, sequentialMode); 724 } 725 else if (attrName.equals(ATTR_SECURITY_LEVEL)) 726 { 727 final String secLevelStr = parseString(p, values, 728 getSecurityLevelName(secLevel)); 729 secLevel = CollectSupportDataSecurityLevel.forName(secLevelStr); 730 } 731 else if (attrName.equals(ATTR_JSTACK_COUNT)) 732 { 733 numJStacks = parseLong(p, values, 734 getIntegerAsLong(numJStacks)).intValue(); 735 } 736 else if (attrName.equals(ATTR_REPORT_COUNT)) 737 { 738 numReports = parseLong(p, values, 739 getIntegerAsLong(numReports)).intValue(); 740 } 741 else if (attrName.equals(ATTR_REPORT_INTERVAL_SECONDS)) 742 { 743 reportIntervalSecs = parseLong(p, values, 744 getIntegerAsLong(reportIntervalSecs)).intValue(); 745 } 746 else if (attrName.equals(ATTR_COMMENT)) 747 { 748 commentStr = parseString(p, values, commentStr); 749 } 750 else if (attrName.equals(ATTR_ENCRYPTION_PASSPHRASE_FILE)) 751 { 752 encPWFile = parseString(p, values, encPWFile); 753 } 754 else if (attrName.equals(ATTR_LOG_DURATION)) 755 { 756 logDurationStr = parseString(p, values, logDurationStr); 757 try 758 { 759 DurationArgument.parseDuration(logDurationStr, TimeUnit.MILLISECONDS); 760 } 761 catch (final Exception e) 762 { 763 Debug.debugException(e); 764 throw new TaskException( 765 ERR_CSD_PROPERTY_INVALID_DURATION.get(logDurationStr, 766 ATTR_LOG_DURATION), 767 e); 768 } 769 } 770 else if (attrName.equals(ATTR_OUTPUT_PATH)) 771 { 772 outputPathStr = parseString(p, values, outputPathStr); 773 } 774 else if (attrName.equals(ATTR_RETAIN_PREVIOUS_ARCHIVE_COUNT)) 775 { 776 retainCount = parseLong(p, values, 777 getIntegerAsLong(retainCount)).intValue(); 778 } 779 else if (attrName.equals(ATTR_RETAIN_PREVIOUS_ARCHIVE_AGE)) 780 { 781 retainAge = parseString(p, values, retainAge); 782 try 783 { 784 DurationArgument.parseDuration(retainAge, TimeUnit.MILLISECONDS); 785 } 786 catch (final Exception e) 787 { 788 Debug.debugException(e); 789 throw new TaskException( 790 ERR_CSD_PROPERTY_INVALID_DURATION.get(retainAge, 791 ATTR_RETAIN_PREVIOUS_ARCHIVE_AGE), 792 e); 793 } 794 } 795 } 796 797 includeBinaryFiles = includeBinary; 798 includeExpensiveData = includeExpensive; 799 includeReplicationStateDump = includeReplicationState; 800 includeExtensionSource = includeSource; 801 useSequentialMode = sequentialMode; 802 securityLevel = secLevel; 803 jstackCount = numJStacks; 804 reportCount = numReports; 805 reportIntervalSeconds = reportIntervalSecs; 806 retainPreviousSupportDataArchiveCount = retainCount; 807 comment = commentStr; 808 encryptionPassphraseFile = encPWFile; 809 logDuration = logDurationStr; 810 outputPath = outputPathStr; 811 retainPreviousSupportDataArchiveAge = retainAge; 812 } 813 814 815 816 /** 817 * Retrieves the name of the provided security level. 818 * 819 * @param securityLevel The security level for which to retrieve the name. 820 * It may be {@code null}. 821 * 822 * @return The name of the provided security level, or {@code null} if the 823 * provided security level is {@code null}. 824 */ 825 static String getSecurityLevelName( 826 final CollectSupportDataSecurityLevel securityLevel) 827 { 828 if (securityLevel == null) 829 { 830 return null; 831 } 832 else 833 { 834 return securityLevel.getName(); 835 } 836 } 837 838 839 840 /** 841 * Retrieves the value of the provided {@code Integer} object as a 842 * {@code Long}. 843 * 844 * @param i The {@code Integer} value to convert to a {@code Long}. It may 845 * be {@code null}. 846 * 847 * @return The value of the provided {@code Integer} object as a 848 * {@code Long}, or {@code null} if the provided value was 849 * {@code null}. 850 */ 851 static Long getIntegerAsLong(final Integer i) 852 { 853 if (i == null) 854 { 855 return null; 856 } 857 else 858 { 859 return i.longValue(); 860 } 861 } 862 863 864 865 /** 866 * {@inheritDoc} 867 */ 868 @Override() 869 public String getTaskName() 870 { 871 return INFO_CSD_TASK_NAME.get(); 872 } 873 874 875 876 /** 877 * {@inheritDoc} 878 */ 879 @Override() 880 public String getTaskDescription() 881 { 882 return INFO_CSD_TASK_DESCRIPTION.get(); 883 } 884 885 886 887 /** 888 * Retrieves the path on the server filesystem to which the support data 889 * archive should be written. 890 * 891 * @return The path on the server filesystem to which the support data 892 * archive should be written, or {@code null} if no value has been 893 * specified for the property. 894 */ 895 public String getOutputPath() 896 { 897 return outputPath; 898 } 899 900 901 902 /** 903 * Retrieves the path on the server filesystem to a file that contains the 904 * passphrase to use to encrypt the support data archive. 905 * 906 * @return The path on the server filesystem to a file that contains the 907 * passphrase to use to encrypt the support data archive, or 908 * {@code null} if no value has been specified for the property, and 909 * the support data archive should not be encrypted. 910 */ 911 public String getEncryptionPassphraseFile() 912 { 913 return encryptionPassphraseFile; 914 } 915 916 917 918 /** 919 * Retrieves the value of a flag that indicates whether the support data 920 * archive may include data that is potentially expensive to collect and 921 * could affect the performance or responsiveness of the server. 922 * 923 * @return The value of a flag that indicates whether the support data 924 * archive may include data that is potentially expensive to collect, 925 * or {@code null} if the property should not be specified when the 926 * task is created (in which case the server will use a default 927 * behavior of excluding expensive data). 928 */ 929 public Boolean getIncludeExpensiveData() 930 { 931 return includeExpensiveData; 932 } 933 934 935 936 /** 937 * Retrieves the value of a flag that indicates whether the support data 938 * archive may include a replication state dump, which may be several 939 * megabytes in size. 940 * 941 * @return The value of a flag that indicates whether the support data 942 * archive may include a replication state dump, or {@code null} if 943 * the property should not be specified when the task is created (in 944 * which case the server will use a default behavior of excluding the 945 * state dump). 946 */ 947 public Boolean getIncludeReplicationStateDump() 948 { 949 return includeReplicationStateDump; 950 } 951 952 953 954 /** 955 * Retrieves the value of a flag that indicates whether the support data 956 * archive may include binary files. 957 * 958 * @return The value of a flag that indicates whether the support data 959 * archive may include binary files, or {@code null} if the property 960 * should not be specified when the task is created (in which case 961 * the server will use a default behavior of excluding binary files). 962 */ 963 public Boolean getIncludeBinaryFiles() 964 { 965 return includeBinaryFiles; 966 } 967 968 969 970 /** 971 * Retrieves the value of a flag that indicates whether the support data 972 * archive should include source code (if available) for any third-party 973 * extensions installed in the server. 974 * 975 * @return The value of a flag that indicates whether the support data 976 * archive should include source code (if available) for any 977 * third-party extensions installed in the server, or {@code null} if 978 * the property should not be specified when the task is created (in 979 * which case the server will use a default behavior of excluding 980 * extension source code). 981 */ 982 public Boolean getIncludeExtensionSource() 983 { 984 return includeExtensionSource; 985 } 986 987 988 989 /** 990 * Retrieves the value of a flag that indicates whether the server should 991 * collect items for the support data archive in sequential mode rather than 992 * in parallel. Collecting data in sequential mode may reduce the amount of 993 * memory consumed during the collection process, but it will take longer to 994 * complete. 995 * 996 * @return The value of a flag that indicates whether the server should 997 * collect items for the support data archive in sequential mode 998 * rather than in parallel, or {@code null} if the property should 999 * not be specified when the task is created (in which case the 1000 * server will default to capturing data in parallel). 1001 */ 1002 public Boolean getUseSequentialMode() 1003 { 1004 return useSequentialMode; 1005 } 1006 1007 1008 1009 /** 1010 * Retrieves the security level that should be used to indicate which data 1011 * should be obscured, redacted, or omitted from the support data archive. 1012 * 1013 * @return The security level that should be used when creating the support 1014 * data archive, or {@code null} if the property should not be 1015 * specified when the task is created (in which case the server will 1016 * use a default security level). 1017 */ 1018 public CollectSupportDataSecurityLevel getSecurityLevel() 1019 { 1020 return securityLevel; 1021 } 1022 1023 1024 1025 /** 1026 * Retrieves the number of intervals that should be captured from tools that 1027 * use interval-based sampling (e.g., vmstat, iostat, mpstat, etc.). 1028 * 1029 * @return The number of intervals that should be captured from tools that 1030 * use interval-based sampling, or {@code null} if the property 1031 * should not be specified when the task is created (in which case 1032 * the server will use a default report count). 1033 */ 1034 public Integer getReportCount() 1035 { 1036 return reportCount; 1037 } 1038 1039 1040 1041 /** 1042 * Retrieves the interval duration in seconds that should be used for tools 1043 * that use interval-based sampling (e.g., vmstat, iostat, mpstat, etc.). 1044 * 1045 * @return The interval duration in seconds that should be used for tools 1046 * that use interval-based sampling, or {@code null} if the property 1047 * should not be specified when the task is created (in which case 1048 * the server will use a default report interval). 1049 */ 1050 public Integer getReportIntervalSeconds() 1051 { 1052 return reportIntervalSeconds; 1053 } 1054 1055 1056 1057 /** 1058 * Retrieves the number of times that the jstack utility should be invoked to 1059 * obtain stack traces from all threads in the server. 1060 * 1061 * @return The number of times that the jstack utility should be invoked to 1062 * obtain stack traces from all threads in the server, or 1063 * {@code null} if the property should not be specified when the task 1064 * is created (in which case the server will use a default count). 1065 */ 1066 public Integer getJStackCount() 1067 { 1068 return jstackCount; 1069 } 1070 1071 1072 1073 /** 1074 * Retrieves a string representation of the duration (up until the time that 1075 * the collect support data task is invoked) of log content that should be 1076 * included in the support data archive. 1077 * 1078 * @return A string representation of the duration of log content that should 1079 * be included in the support data archive, or {@code null} if the 1080 * property should not be specified when the task is created (in 1081 * which case the server will use a default behavior for selecting 1082 * the amount of log content to include). 1083 */ 1084 public String getLogDuration() 1085 { 1086 return logDuration; 1087 } 1088 1089 1090 1091 /** 1092 * Retrieves a parsed value of the log duration in milliseconds. 1093 * 1094 * @return A parsed value of the log duration in milliseconds or {@code null} 1095 * if no log duration is set. 1096 * 1097 * @throws TaskException If the log duration value cannot be parsed as a 1098 * valid duration. 1099 */ 1100 public Long getLogDurationMillis() 1101 throws TaskException 1102 { 1103 if (logDuration == null) 1104 { 1105 return null; 1106 } 1107 1108 try 1109 { 1110 return DurationArgument.parseDuration(logDuration, TimeUnit.MILLISECONDS); 1111 } 1112 catch (final ArgumentException e) 1113 { 1114 Debug.debugException(e); 1115 throw new TaskException(e.getMessage(), e); 1116 } 1117 } 1118 1119 1120 1121 /** 1122 * Retrieves an additional comment that should be included in the support data 1123 * archive. 1124 * 1125 * @return An additional comment that should be included in the support data 1126 * archive, or {@code null} if no comment should be included. 1127 */ 1128 public String getComment() 1129 { 1130 return comment; 1131 } 1132 1133 1134 1135 /** 1136 * Retrieves the minimum number of existing support data archives that should 1137 * be retained. 1138 * 1139 * @return The minimum number of existing support data archives that should 1140 * be retained, or {@code null} if there is no minimum retain count. 1141 */ 1142 public Integer getRetainPreviousSupportDataArchiveCount() 1143 { 1144 return retainPreviousSupportDataArchiveCount; 1145 } 1146 1147 1148 1149 /** 1150 * Retrieves the minimum age of existing support data archives that should be 1151 * retained. 1152 * 1153 * @return The minimum age of existing support data archives that should 1154 * be retained, or {@code null} if there is no minimum retain age. 1155 */ 1156 public String getRetainPreviousSupportDataArchiveAge() 1157 { 1158 return retainPreviousSupportDataArchiveAge; 1159 } 1160 1161 1162 1163 /** 1164 * Retrieves a parsed value of the retain previous support data archive age in 1165 * milliseconds. 1166 * 1167 * @return A parsed value of the retain previous support data archive age in 1168 * milliseconds or {@code null} if no retain age is set. 1169 * 1170 * @throws TaskException If the retain age value cannot be parsed as a valid 1171 * duration. 1172 */ 1173 public Long getRetainPreviousSupportDataArchiveAgeMillis() 1174 throws TaskException 1175 { 1176 if (retainPreviousSupportDataArchiveAge == null) 1177 { 1178 return null; 1179 } 1180 1181 try 1182 { 1183 return DurationArgument.parseDuration( 1184 retainPreviousSupportDataArchiveAge, TimeUnit.MILLISECONDS); 1185 } 1186 catch (final ArgumentException e) 1187 { 1188 Debug.debugException(e); 1189 throw new TaskException(e.getMessage(), e); 1190 } 1191 } 1192 1193 1194 1195 /** 1196 * {@inheritDoc} 1197 */ 1198 @Override() 1199 protected List<String> getAdditionalObjectClasses() 1200 { 1201 return Collections.singletonList(OC_COLLECT_SUPPORT_DATA_TASK); 1202 } 1203 1204 1205 1206 /** 1207 * {@inheritDoc} 1208 */ 1209 @Override() 1210 protected List<Attribute> getAdditionalAttributes() 1211 { 1212 final List<Attribute> attrList = new ArrayList<>(15); 1213 1214 if (outputPath != null) 1215 { 1216 attrList.add(new Attribute(ATTR_OUTPUT_PATH, outputPath)); 1217 } 1218 1219 if (encryptionPassphraseFile != null) 1220 { 1221 attrList.add(new Attribute(ATTR_ENCRYPTION_PASSPHRASE_FILE, 1222 encryptionPassphraseFile)); 1223 } 1224 1225 if (includeExpensiveData != null) 1226 { 1227 attrList.add(new Attribute(ATTR_INCLUDE_EXPENSIVE_DATA, 1228 includeExpensiveData ? "TRUE" : "FALSE")); 1229 } 1230 1231 if (includeReplicationStateDump != null) 1232 { 1233 attrList.add(new Attribute(ATTR_INCLUDE_REPLICATION_STATE_DUMP, 1234 includeReplicationStateDump ? "TRUE" : "FALSE")); 1235 } 1236 1237 if (includeBinaryFiles != null) 1238 { 1239 attrList.add(new Attribute(ATTR_INCLUDE_BINARY_FILES, 1240 includeBinaryFiles ? "TRUE" : "FALSE")); 1241 } 1242 1243 if (includeExtensionSource != null) 1244 { 1245 attrList.add(new Attribute(ATTR_INCLUDE_EXTENSION_SOURCE, 1246 includeExtensionSource ? "TRUE" : "FALSE")); 1247 } 1248 1249 if (useSequentialMode != null) 1250 { 1251 attrList.add(new Attribute(ATTR_USE_SEQUENTIAL_MODE, 1252 useSequentialMode ? "TRUE" : "FALSE")); 1253 } 1254 1255 if (securityLevel != null) 1256 { 1257 attrList.add(new Attribute(ATTR_SECURITY_LEVEL, securityLevel.getName())); 1258 } 1259 1260 if (jstackCount != null) 1261 { 1262 attrList.add(new Attribute(ATTR_JSTACK_COUNT, 1263 String.valueOf(jstackCount))); 1264 } 1265 1266 if (reportCount != null) 1267 { 1268 attrList.add(new Attribute(ATTR_REPORT_COUNT, 1269 String.valueOf(reportCount))); 1270 } 1271 1272 if (reportIntervalSeconds != null) 1273 { 1274 attrList.add(new Attribute(ATTR_REPORT_INTERVAL_SECONDS, 1275 String.valueOf(reportIntervalSeconds))); 1276 } 1277 1278 if (logDuration != null) 1279 { 1280 attrList.add(new Attribute(ATTR_LOG_DURATION, logDuration)); 1281 } 1282 1283 if (comment != null) 1284 { 1285 attrList.add(new Attribute(ATTR_COMMENT, comment)); 1286 } 1287 1288 if (retainPreviousSupportDataArchiveCount != null) 1289 { 1290 attrList.add(new Attribute(ATTR_RETAIN_PREVIOUS_ARCHIVE_COUNT, 1291 String.valueOf(retainPreviousSupportDataArchiveCount))); 1292 } 1293 1294 if (retainPreviousSupportDataArchiveAge != null) 1295 { 1296 attrList.add(new Attribute(ATTR_RETAIN_PREVIOUS_ARCHIVE_AGE, 1297 retainPreviousSupportDataArchiveAge)); 1298 } 1299 1300 return attrList; 1301 } 1302 1303 1304 1305 /** 1306 * {@inheritDoc} 1307 */ 1308 @Override() 1309 public List<TaskProperty> getTaskSpecificProperties() 1310 { 1311 return Collections.unmodifiableList(Arrays.asList( 1312 PROPERTY_OUTPUT_PATH, 1313 PROPERTY_ENCRYPTION_PASSPHRASE_FILE, 1314 PROPERTY_INCLUDE_EXPENSIVE_DATA, 1315 PROPERTY_INCLUDE_REPLICATION_STATE_DUMP, 1316 PROPERTY_INCLUDE_BINARY_FILES, 1317 PROPERTY_INCLUDE_EXTENSION_SOURCE, 1318 PROPERTY_USE_SEQUENTIAL_MODE, 1319 PROPERTY_SECURITY_LEVEL, 1320 PROPERTY_JSTACK_COUNT, 1321 PROPERTY_REPORT_COUNT, 1322 PROPERTY_REPORT_INTERVAL_SECONDS, 1323 PROPERTY_LOG_DURATION, 1324 PROPERTY_COMMENT, 1325 PROPERTY_RETAIN_PREVIOUS_ARCHIVE_COUNT, 1326 PROPERTY_RETAIN_PREVIOUS_ARCHIVE_AGE)); 1327 } 1328 1329 1330 1331 /** 1332 * {@inheritDoc} 1333 */ 1334 @Override() 1335 public Map<TaskProperty,List<Object>> getTaskPropertyValues() 1336 { 1337 final Map<TaskProperty,List<Object>> props = 1338 new LinkedHashMap<>(StaticUtils.computeMapCapacity(20)); 1339 1340 if (outputPath != null) 1341 { 1342 props.put(PROPERTY_OUTPUT_PATH, 1343 Collections.<Object>singletonList(outputPath)); 1344 } 1345 1346 if (encryptionPassphraseFile != null) 1347 { 1348 props.put(PROPERTY_ENCRYPTION_PASSPHRASE_FILE, 1349 Collections.<Object>singletonList(encryptionPassphraseFile)); 1350 } 1351 1352 if (includeExpensiveData != null) 1353 { 1354 props.put(PROPERTY_INCLUDE_EXPENSIVE_DATA, 1355 Collections.<Object>singletonList(includeExpensiveData)); 1356 } 1357 1358 if (includeReplicationStateDump != null) 1359 { 1360 props.put(PROPERTY_INCLUDE_REPLICATION_STATE_DUMP, 1361 Collections.<Object>singletonList(includeReplicationStateDump)); 1362 } 1363 1364 if (includeBinaryFiles != null) 1365 { 1366 props.put(PROPERTY_INCLUDE_BINARY_FILES, 1367 Collections.<Object>singletonList(includeBinaryFiles)); 1368 } 1369 1370 if (includeExtensionSource != null) 1371 { 1372 props.put(PROPERTY_INCLUDE_EXTENSION_SOURCE, 1373 Collections.<Object>singletonList(includeExtensionSource)); 1374 } 1375 1376 if (useSequentialMode != null) 1377 { 1378 props.put(PROPERTY_USE_SEQUENTIAL_MODE, 1379 Collections.<Object>singletonList(useSequentialMode)); 1380 } 1381 1382 if (securityLevel != null) 1383 { 1384 props.put(PROPERTY_SECURITY_LEVEL, 1385 Collections.<Object>singletonList(securityLevel.getName())); 1386 } 1387 1388 if (jstackCount != null) 1389 { 1390 props.put(PROPERTY_JSTACK_COUNT, 1391 Collections.<Object>singletonList(jstackCount.longValue())); 1392 } 1393 1394 if (reportCount != null) 1395 { 1396 props.put(PROPERTY_REPORT_COUNT, 1397 Collections.<Object>singletonList(reportCount.longValue())); 1398 } 1399 1400 if (reportIntervalSeconds != null) 1401 { 1402 props.put(PROPERTY_REPORT_INTERVAL_SECONDS, 1403 Collections.<Object>singletonList( 1404 reportIntervalSeconds.longValue())); 1405 } 1406 1407 if (logDuration != null) 1408 { 1409 props.put(PROPERTY_LOG_DURATION, 1410 Collections.<Object>singletonList(logDuration)); 1411 } 1412 1413 if (comment != null) 1414 { 1415 props.put(PROPERTY_COMMENT, 1416 Collections.<Object>singletonList(comment)); 1417 } 1418 1419 if (retainPreviousSupportDataArchiveCount != null) 1420 { 1421 props.put(PROPERTY_RETAIN_PREVIOUS_ARCHIVE_COUNT, 1422 Collections.<Object>singletonList( 1423 retainPreviousSupportDataArchiveCount.longValue())); 1424 } 1425 1426 if (retainPreviousSupportDataArchiveAge != null) 1427 { 1428 props.put(PROPERTY_RETAIN_PREVIOUS_ARCHIVE_AGE, 1429 Collections.<Object>singletonList( 1430 retainPreviousSupportDataArchiveAge)); 1431 } 1432 1433 props.putAll(super.getTaskPropertyValues()); 1434 return Collections.unmodifiableMap(props); 1435 } 1436}