001/* 002 * Copyright 2018-2019 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2018-2019 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.tasks; 022 023 024 025import java.util.Arrays; 026import java.util.Collections; 027import java.util.Date; 028import java.util.LinkedHashMap; 029import java.util.LinkedList; 030import java.util.List; 031import java.util.Map; 032import java.util.concurrent.TimeUnit; 033 034import com.unboundid.ldap.sdk.Attribute; 035import com.unboundid.ldap.sdk.Entry; 036import com.unboundid.util.Debug; 037import com.unboundid.util.NotMutable; 038import com.unboundid.util.StaticUtils; 039import com.unboundid.util.ThreadSafety; 040import com.unboundid.util.ThreadSafetyLevel; 041import com.unboundid.util.Validator; 042import com.unboundid.util.args.DurationArgument; 043 044import static com.unboundid.ldap.sdk.unboundidds.tasks.TaskMessages.*; 045 046 047 048/** 049 * This class defines a Directory Server task that can be used to identify files 050 * in a specified directory that match a given pattern, and delete any of those 051 * files that are outside of a provided set of retention criteria. 052 * <BR> 053 * <BLOCKQUOTE> 054 * <B>NOTE:</B> This class, and other classes within the 055 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 056 * supported for use against Ping Identity, UnboundID, and 057 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 058 * for proprietary functionality or for external specifications that are not 059 * considered stable or mature enough to be guaranteed to work in an 060 * interoperable way with other types of LDAP servers. 061 * </BLOCKQUOTE> 062 * <BR> 063 * The files to examine are identified by a combination of three items: 064 * <UL> 065 * <LI>A target directory. This is simply the path to the directory that 066 * contains the files to examine.</LI> 067 * <LI>A filename pattern. This is a string that will be used to identify 068 * the files of interest in the target directory. The pattern may contain 069 * zero or more (non-consecutive) asterisks to use as wildcards that match 070 * zero or more characters, and it may contain at most one occurrence of 071 * the token "${timestamp}" (without the quotation marks) that is a 072 * placeholder for a timestamp that indicates when the file was written or 073 * the age of the data in that file. For example, the filename pattern 074 * "*-${timestamp}.log" will match any file in the target directory that 075 * ends with a dash, a timestamp, and an extension of ".log".</LI> 076 * <LI>A timestamp format. This specifies the format that will be used for 077 * the value that matches the "${timestamp}" token in the filename 078 * pattern. See the {@link FileRetentionTaskTimestampFormat} enum for the 079 * set of defined timestamp formats.</LI> 080 * </UL> 081 * <BR> 082 * The types of retention criteria include: 083 * <UL> 084 * <LI>A retain count, which specifies the minimum number of files to retain. 085 * For example, if there is a retain count of five, and the target 086 * directory contains ten files that match the filename pattern, the task 087 * will always keep at least the five most recent files, while the five 088 * oldest files will be candidates for removal.</LI> 089 * <LI>A retain age, which specifies the minimum age of the files to retain. 090 * If the filename pattern includes a timestamp, then the age of the file 091 * will be determined using that timestamp. If the filename pattern does 092 * not contain a timestamp, then the age of the file will be determined 093 * from the file's create time attribute (if available) or last modified 094 * time. The task will always keep all files whose age is less than or 095 * equal to the retain age, while files older than the retain age will be 096 * candidates for removal.</LI> 097 * <LI>An aggregate retain size, which specifies combined minimum amount of 098 * disk space that should be consumed by the files that should be 099 * retained. For example, if the task is configured with an aggregate 100 * retain size of 500 megabytes and the files to examine are all 75 101 * megabytes each, then the task will keep at least the seven most recent 102 * files (because 500/75 = 6.7, and the task will always round up to the 103 * next whole number), and any older files in the same directory that 104 * match the pattern will be candidates for removal. 105 * </UL> 106 * <BR> 107 * The task must be configured with at least one of the three types of retention 108 * criteria, but it may combine any two or all three of them. If a task is 109 * configured with multiple types of retention criteria, then a file will only 110 * be a candidate for removal if it is outside of all of the retention criteria. 111 * For example, if the task is configured with a retain count of 5 and a retain 112 * age of 1 week, then the task may retain more than five files if there are 113 * more than five files that are less than a week old, and it may retain files 114 * that are more than a week old if there are fewer than five files within that 115 * age. 116 */ 117@NotMutable() 118@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 119public final class FileRetentionTask 120 extends Task 121{ 122 /** 123 * The fully-qualified name of the Java class that is used for the file 124 * retention task. 125 */ 126 static final String FILE_RETENTION_TASK_CLASS = 127 "com.unboundid.directory.server.tasks.FileRetentionTask"; 128 129 130 131 /** 132 * The name of the attribute that is used to specify the path to the directory 133 * containing the files to delete. 134 */ 135 private static final String ATTR_TARGET_DIRECTORY = 136 "ds-task-file-retention-target-directory"; 137 138 139 140 /** 141 * The name of the attribute that is used to specify the filename pattern that 142 * is used to identify the files to examine. 143 */ 144 private static final String ATTR_FILENAME_PATTERN = 145 "ds-task-file-retention-filename-pattern"; 146 147 148 149 /** 150 * The name of the attribute that is used to specify the format to use for 151 * timestamp values in the filename pattern. 152 */ 153 private static final String ATTR_TIMESTAMP_FORMAT = 154 "ds-task-file-retention-timestamp-format"; 155 156 157 158 /** 159 * The name of the attribute that is used to specify the minimum number of 160 * files to retain. 161 */ 162 private static final String ATTR_RETAIN_FILE_COUNT = 163 "ds-task-file-retention-retain-file-count"; 164 165 166 167 /** 168 * The name of the attribute that is used to specify the minimum age of 169 * files to retain. 170 */ 171 private static final String ATTR_RETAIN_FILE_AGE = 172 "ds-task-file-retention-retain-file-age"; 173 174 175 176 /** 177 * The name of the attribute that is used to specify the minimum aggregate 178 * size, in bytes, of files to retain. 179 */ 180 private static final String ATTR_RETAIN_AGGREGATE_FILE_SIZE_BYTES = 181 "ds-task-file-retention-retain-aggregate-file-size-bytes"; 182 183 184 185 /** 186 * The name of the object class used in file retention task entries. 187 */ 188 private static final String OC_FILE_RETENTION_TASK = "ds-task-file-retention"; 189 190 191 192 /** 193 * The task property that will be used for the target directory. 194 */ 195 private static final TaskProperty PROPERTY_TARGET_DIRECTORY = 196 new TaskProperty(ATTR_TARGET_DIRECTORY, 197 INFO_FILE_RETENTION_DISPLAY_NAME_TARGET_DIRECTORY.get(), 198 INFO_FILE_RETENTION_DESCRIPTION_TARGET_DIRECTORY.get(), String.class, 199 true, false, false); 200 201 202 203 /** 204 * The task property that will be used for the filename pattern. 205 */ 206 private static final TaskProperty PROPERTY_FILENAME_PATTERN = 207 new TaskProperty(ATTR_FILENAME_PATTERN, 208 INFO_FILE_RETENTION_DISPLAY_NAME_FILENAME_PATTERN.get(), 209 INFO_FILE_RETENTION_DESCRIPTION_FILENAME_PATTERN.get(), String.class, 210 true, false, false); 211 212 213 214 /** 215 * The task property that will be used for the timestamp format. 216 */ 217 private static final TaskProperty PROPERTY_TIMESTAMP_FORMAT = 218 new TaskProperty(ATTR_TIMESTAMP_FORMAT, 219 INFO_FILE_RETENTION_DISPLAY_NAME_TIMESTAMP_FORMAT.get(), 220 INFO_FILE_RETENTION_DESCRIPTION_TIMESTAMP_FORMAT.get(), String.class, 221 true, false, false, 222 new String[] 223 { 224 FileRetentionTaskTimestampFormat. 225 GENERALIZED_TIME_UTC_WITH_MILLISECONDS.name(), 226 FileRetentionTaskTimestampFormat. 227 GENERALIZED_TIME_UTC_WITH_SECONDS.name(), 228 FileRetentionTaskTimestampFormat. 229 GENERALIZED_TIME_UTC_WITH_MINUTES.name(), 230 FileRetentionTaskTimestampFormat. 231 LOCAL_TIME_WITH_MILLISECONDS.name(), 232 FileRetentionTaskTimestampFormat.LOCAL_TIME_WITH_SECONDS.name(), 233 FileRetentionTaskTimestampFormat.LOCAL_TIME_WITH_MINUTES.name(), 234 FileRetentionTaskTimestampFormat.LOCAL_DATE.name() 235 }); 236 237 238 239 /** 240 * The task property that will be used for the file retention count. 241 */ 242 private static final TaskProperty PROPERTY_RETAIN_FILE_COUNT = 243 new TaskProperty(ATTR_RETAIN_FILE_COUNT, 244 INFO_FILE_RETENTION_DISPLAY_NAME_RETAIN_COUNT.get(), 245 INFO_FILE_RETENTION_DESCRIPTION_RETAIN_COUNT.get(), Long.class, 246 false, false, false); 247 248 249 250 /** 251 * The task property that will be used for the file retention age. 252 */ 253 private static final TaskProperty PROPERTY_RETAIN_FILE_AGE_MILLIS = 254 new TaskProperty(ATTR_RETAIN_FILE_AGE, 255 INFO_FILE_RETENTION_DISPLAY_NAME_RETAIN_AGE.get(), 256 INFO_FILE_RETENTION_DESCRIPTION_RETAIN_AGE.get(), Long.class, 257 false, false, false); 258 259 260 261 /** 262 * The task property that will be used for the file retention size. 263 */ 264 private static final TaskProperty PROPERTY_RETAIN_AGGREGATE_FILE_SIZE_BYTES = 265 new TaskProperty(ATTR_RETAIN_AGGREGATE_FILE_SIZE_BYTES, 266 INFO_FILE_RETENTION_DISPLAY_NAME_RETAIN_SIZE.get(), 267 INFO_FILE_RETENTION_DESCRIPTION_RETAIN_SIZE.get(), Long.class, false, 268 false, false); 269 270 271 272 /** 273 * The serial version UID for this serializable class. 274 */ 275 private static final long serialVersionUID = 7401251158315611295L; 276 277 278 279 // The format for the timestamp that may be used in the filename pattern. 280 private final FileRetentionTaskTimestampFormat timestampFormat; 281 282 // The file retention count. 283 private final Integer retainFileCount; 284 285 // The file retention aggregate size in bytes. 286 private final Long retainAggregateFileSizeBytes; 287 288 // The file retention age in milliseconds. 289 private final Long retainFileAgeMillis; 290 291 // The pattern that identifies the files to examine. 292 private final String filenamePattern; 293 294 // The path to the directory containing the files to examine. 295 private final String targetDirectory; 296 297 298 299 /** 300 * Creates a new, uninitialized file retention task instance that should only 301 * be used for obtaining general information about this task, including the 302 * task name, description, and supported properties. Attempts to use a task 303 * created with this constructor for any other reason will likely fail. 304 */ 305 public FileRetentionTask() 306 { 307 targetDirectory = null; 308 filenamePattern = null; 309 timestampFormat = null; 310 retainFileCount = null; 311 retainFileAgeMillis = null; 312 retainAggregateFileSizeBytes = null; 313 } 314 315 316 317 /** 318 * Creates a new file retention task with the provided information. 319 * 320 * @param targetDirectory 321 * The path to the directory containing the files to examine. 322 * This must be provided, and the target directory must exist on 323 * the server filesystem. 324 * @param filenamePattern 325 * A pattern that identifies the files to examine. The pattern 326 * may include zero or more (non-consecutive) asterisks that act 327 * as wildcards and match zero or more characters. The pattern 328 * may also contain at most one occurrence of the "${timestamp}" 329 * token, which indicates that the filename includes a timestamp 330 * with the format specified in the {@code timestampFormat} 331 * argument. This must not be {@code null} or empty. 332 * @param timestampFormat 333 * The expected format for the timestamp that may appear in the 334 * filename pattern. This must not be {@code null}, even if the 335 * filename pattern does not contain a "${timestamp}" token. 336 * @param retainFileCount 337 * The minimum number of the most recent files that should be 338 * retained. This may be {@code null} if only age-based or 339 * size-based retention criteria should be used. At least one of 340 * the {@code retainFileCount}, {@code retainFileAgeMillis}, and 341 * {@code retainAggregateFileSizeBytes} values must be 342 * non-{@code null}. If this value is non-{@code null}, then it 343 * must be greater than or equal to zero. 344 * @param retainFileAgeMillis 345 * The minimum age, in milliseconds, for files that should be 346 * retained. This may be {@code null} if only count-based or 347 * size-based retention criteria should be used. At least one of 348 * the {@code retainFileCount}, {@code retainFileAgeMillis}, and 349 * {@code retainAggregateFileSizeBytes} values must be 350 * non-{@code null}. If this value is non-{@code null}, then 351 * it must be greater than zero. 352 * @param retainAggregateFileSizeBytes 353 * The minimum amount of disk space, in bytes, that should be 354 * consumed by the files to be retained. This may be 355 * {@code null} if only count-based or age-based retention 356 * criteria should be used. At least one of the 357 * {@code retainFileCount}, {@code retainFileAgeMillis}, and 358 * {@code retainAggregateFileSizeBytes} values must be 359 * non-{@code null}. If this value is non-{@code null}, then it 360 * must be greater than zero. 361 */ 362 public FileRetentionTask(final String targetDirectory, 363 final String filenamePattern, 364 final FileRetentionTaskTimestampFormat timestampFormat, 365 final Integer retainFileCount, final Long retainFileAgeMillis, 366 final Long retainAggregateFileSizeBytes) 367 { 368 this(null, targetDirectory, filenamePattern, timestampFormat, 369 retainFileCount, retainFileAgeMillis, retainAggregateFileSizeBytes, 370 null, null, null, null, null, null, null, null, null, null); 371 } 372 373 374 375 /** 376 * Creates a new file retention task with the provided information. 377 * 378 * @param taskID 379 * The task ID to use for this task. If it is {@code null} then 380 * a UUID will be generated for use as the task ID. 381 * @param targetDirectory 382 * The path to the directory containing the files to examine. 383 * This must be provided, and the target directory must exist on 384 * the server filesystem. 385 * @param filenamePattern 386 * A pattern that identifies the files to examine. The pattern 387 * may include zero or more (non-consecutive) asterisks that act 388 * as wildcards and match zero or more characters. The pattern 389 * may also contain at most one occurrence of the "${timestamp}" 390 * token, which indicates that the filename includes a timestamp 391 * with the format specified in the {@code timestampFormat} 392 * argument. This must not be {@code null} or empty. 393 * @param timestampFormat 394 * The expected format for the timestamp that may appear in the 395 * filename pattern. This must not be {@code null}, even if the 396 * filename pattern does not contain a "${timestamp}" token. 397 * @param retainFileCount 398 * The minimum number of the most recent files that should be 399 * retained. This may be {@code null} if only age-based or 400 * size-based retention criteria should be used. At least one of 401 * the {@code retainFileCount}, {@code retainFileAgeMillis}, and 402 * {@code retainAggregateFileSizeBytes} values must be 403 * non-{@code null}. If this value is non-{@code null}, then it 404 * must be greater than or equal to zero. 405 * @param retainFileAgeMillis 406 * The minimum age, in milliseconds, for files that should be 407 * retained. This may be {@code null} if only count-based or 408 * size-based retention criteria should be used. At least one of 409 * the {@code retainFileCount}, {@code retainFileAgeMillis}, and 410 * {@code retainAggregateFileSizeBytes} values must be 411 * non-{@code null}. If this value is non-{@code null}, then 412 * it must be greater than zero. 413 * @param retainAggregateFileSizeBytes 414 * The minimum amount of disk space, in bytes, that should be 415 * consumed by the files to be retained. This may be 416 * {@code null} if only count-based or age-based retention 417 * criteria should be used. At least one of the 418 * {@code retainFileCount}, {@code retainFileAgeMillis}, and 419 * {@code retainAggregateFileSizeBytes} values must be 420 * non-{@code null}. If this value is non-{@code null}, then it 421 * must be greater than zero. 422 * @param scheduledStartTime 423 * The time that this task should start running. 424 * @param dependencyIDs 425 * The list of task IDs that will be required to complete before 426 * this task will be eligible to start. 427 * @param failedDependencyAction 428 * Indicates what action should be taken if any of the 429 * dependencies for this task do not complete successfully. 430 * @param notifyOnStart 431 * The list of e-mail addresses of individuals that should be 432 * notified when this task starts. 433 * @param notifyOnCompletion 434 * The list of e-mail addresses of individuals that should be 435 * notified when this task completes. 436 * @param notifyOnSuccess 437 * The list of e-mail addresses of individuals that should be 438 * notified if this task completes successfully. 439 * @param notifyOnError 440 * The list of e-mail addresses of individuals that should be 441 * notified if this task does not complete successfully. 442 * @param alertOnStart 443 * Indicates whether the server should send an alert notification 444 * when this task starts. 445 * @param alertOnSuccess 446 * Indicates whether the server should send an alert notification 447 * if this task completes successfully. 448 * @param alertOnError 449 * Indicates whether the server should send an alert notification 450 * if this task fails to complete successfully. 451 */ 452 public FileRetentionTask(final String taskID, final String targetDirectory, 453 final String filenamePattern, 454 final FileRetentionTaskTimestampFormat timestampFormat, 455 final Integer retainFileCount, final Long retainFileAgeMillis, 456 final Long retainAggregateFileSizeBytes, 457 final Date scheduledStartTime, final List<String> dependencyIDs, 458 final FailedDependencyAction failedDependencyAction, 459 final List<String> notifyOnStart, 460 final List<String> notifyOnCompletion, 461 final List<String> notifyOnSuccess, 462 final List<String> notifyOnError, final Boolean alertOnStart, 463 final Boolean alertOnSuccess, final Boolean alertOnError) 464 { 465 super(taskID, FILE_RETENTION_TASK_CLASS, scheduledStartTime, dependencyIDs, 466 failedDependencyAction, notifyOnStart, notifyOnCompletion, 467 notifyOnSuccess, notifyOnError, alertOnStart, alertOnSuccess, 468 alertOnError); 469 470 Validator.ensureNotNullOrEmpty(targetDirectory, 471 "FileRetentionTask.targetDirectory must not be null or empty"); 472 Validator.ensureNotNullOrEmpty(filenamePattern, 473 "FileRetentionTask.filenamePattern must not be null or empty"); 474 Validator.ensureNotNullWithMessage(timestampFormat, 475 "FileRetentionTask.timestampFormat must not be null"); 476 477 Validator.ensureTrue( 478 ((retainFileCount != null) || (retainFileAgeMillis != null) || 479 (retainAggregateFileSizeBytes != null)), 480 "At least one of retainFileCount, retainFileAgeMillis, and " + 481 "retainAggregateFileSizeBytes must be non-null"); 482 483 Validator.ensureTrue( 484 ((retainFileCount == null) || (retainFileCount >= 0)), 485 "FileRetentionTask.retainFileCount must not be negative"); 486 Validator.ensureTrue( 487 ((retainFileAgeMillis == null) || (retainFileAgeMillis > 0L)), 488 "FileRetentionTask.retainFileAgeMillis must not be negative or zero"); 489 Validator.ensureTrue( 490 ((retainAggregateFileSizeBytes == null) || 491 (retainAggregateFileSizeBytes > 0L)), 492 "FileRetentionTask.retainAggregateFileSizeBytes must not be " + 493 "negative or zero"); 494 495 this.targetDirectory = targetDirectory; 496 this.filenamePattern = filenamePattern; 497 this.timestampFormat = timestampFormat; 498 this.retainFileCount = retainFileCount; 499 this.retainFileAgeMillis = retainFileAgeMillis; 500 this.retainAggregateFileSizeBytes = retainAggregateFileSizeBytes; 501 } 502 503 504 505 /** 506 * Creates a new file retention task from the provided entry. 507 * 508 * @param entry The entry to use to create this file retention task. 509 * 510 * @throws TaskException If the provided entry cannot be parsed as a file 511 * retention task entry. 512 */ 513 public FileRetentionTask(final Entry entry) 514 throws TaskException 515 { 516 super(entry); 517 518 // Get the path to the target directory. It must not be null or empty. 519 targetDirectory = entry.getAttributeValue(ATTR_TARGET_DIRECTORY); 520 if ((targetDirectory == null) || targetDirectory.isEmpty()) 521 { 522 throw new TaskException( 523 ERR_FILE_RETENTION_ENTRY_MISSING_REQUIRED_ATTR.get(entry.getDN(), 524 ATTR_TARGET_DIRECTORY)); 525 } 526 527 528 // Get the path to the filename pattern. It must not be null or empty. 529 filenamePattern = entry.getAttributeValue(ATTR_FILENAME_PATTERN); 530 if ((filenamePattern == null) || filenamePattern.isEmpty()) 531 { 532 throw new TaskException( 533 ERR_FILE_RETENTION_ENTRY_MISSING_REQUIRED_ATTR.get(entry.getDN(), 534 ATTR_FILENAME_PATTERN)); 535 } 536 537 538 // Get the timestamp format. It must not be null, and must be a valid 539 // format. 540 final String timestampFormatName = 541 entry.getAttributeValue(ATTR_TIMESTAMP_FORMAT); 542 if (timestampFormatName == null) 543 { 544 throw new TaskException( 545 ERR_FILE_RETENTION_ENTRY_MISSING_REQUIRED_ATTR.get(entry.getDN(), 546 ATTR_TIMESTAMP_FORMAT)); 547 } 548 549 timestampFormat = 550 FileRetentionTaskTimestampFormat.forName(timestampFormatName); 551 if (timestampFormat == null) 552 { 553 final StringBuilder validFormats = new StringBuilder(); 554 for (final FileRetentionTaskTimestampFormat f : 555 FileRetentionTaskTimestampFormat.values()) 556 { 557 if (validFormats.length() > 0) 558 { 559 validFormats.append(", "); 560 } 561 562 validFormats.append(f.name()); 563 } 564 565 throw new TaskException( 566 ERR_FILE_RETENTION_ENTRY_INVALID_TIMESTAMP_FORMAT.get( 567 entry.getDN(), timestampFormatName, validFormats.toString())); 568 } 569 570 571 // Get the retain file count. If it is non-null, then it must also be 572 // non-negative. 573 final String retainFileCountString = 574 entry.getAttributeValue(ATTR_RETAIN_FILE_COUNT); 575 if (retainFileCountString == null) 576 { 577 retainFileCount = null; 578 } 579 else 580 { 581 try 582 { 583 retainFileCount = Integer.parseInt(retainFileCountString); 584 } 585 catch (final Exception e) 586 { 587 Debug.debugException(e); 588 throw new TaskException( 589 ERR_FILE_RETENTION_ENTRY_INVALID_RETAIN_COUNT.get( 590 entry.getDN(), retainFileCountString), 591 e); 592 } 593 594 if (retainFileCount < 0) 595 { 596 throw new TaskException( 597 ERR_FILE_RETENTION_ENTRY_INVALID_RETAIN_COUNT.get( 598 entry.getDN(), retainFileCountString)); 599 } 600 } 601 602 603 // Get the retain file age in milliseconds. 604 final String retainFileAgeString = 605 entry.getAttributeValue(ATTR_RETAIN_FILE_AGE); 606 if (retainFileAgeString == null) 607 { 608 retainFileAgeMillis = null; 609 } 610 else 611 { 612 try 613 { 614 retainFileAgeMillis = DurationArgument.parseDuration( 615 retainFileAgeString, TimeUnit.MILLISECONDS); 616 } 617 catch (final Exception e) 618 { 619 Debug.debugException(e); 620 throw new TaskException( 621 ERR_FILE_RETENTION_ENTRY_INVALID_RETAIN_AGE.get( 622 entry.getDN(), retainFileAgeString, 623 StaticUtils.getExceptionMessage(e)), 624 e); 625 } 626 } 627 628 629 // Get the retain aggregate file size in bytes. If it is non-null, then it 630 // must also be positive. 631 final String retainAggregateFileSizeBytesString = 632 entry.getAttributeValue(ATTR_RETAIN_AGGREGATE_FILE_SIZE_BYTES); 633 if (retainAggregateFileSizeBytesString == null) 634 { 635 retainAggregateFileSizeBytes = null; 636 } 637 else 638 { 639 try 640 { 641 retainAggregateFileSizeBytes = 642 Long.parseLong(retainAggregateFileSizeBytesString); 643 } 644 catch (final Exception e) 645 { 646 Debug.debugException(e); 647 throw new TaskException( 648 ERR_FILE_RETENTION_ENTRY_INVALID_RETAIN_SIZE.get( 649 entry.getDN(), retainAggregateFileSizeBytesString), 650 e); 651 } 652 653 if (retainAggregateFileSizeBytes <= 0) 654 { 655 throw new TaskException( 656 ERR_FILE_RETENTION_ENTRY_INVALID_RETAIN_SIZE.get( 657 entry.getDN(), retainAggregateFileSizeBytesString)); 658 } 659 } 660 661 if ((retainFileCount == null) && (retainFileAgeMillis == null) && 662 (retainAggregateFileSizeBytes == null)) 663 { 664 throw new TaskException( 665 ERR_FILE_RETENTION_ENTRY_MISSING_RETENTION_CRITERIA.get( 666 entry.getDN(), ATTR_RETAIN_FILE_COUNT, ATTR_RETAIN_FILE_AGE, 667 ATTR_RETAIN_AGGREGATE_FILE_SIZE_BYTES)); 668 } 669 } 670 671 672 673 /** 674 * Creates a new file retention task from the provided set of task properties. 675 * 676 * @param properties The set of task properties and their corresponding 677 * values to use for the task. It must not be 678 * {@code null}. 679 * 680 * @throws TaskException If the provided set of properties cannot be used to 681 * create a valid file retention task. 682 */ 683 public FileRetentionTask(final Map<TaskProperty,List<Object>> properties) 684 throws TaskException 685 { 686 super(FILE_RETENTION_TASK_CLASS, properties); 687 688 String directory = null; 689 String pattern = null; 690 FileRetentionTaskTimestampFormat format = null; 691 Long count = null; 692 Long age = null; 693 Long size = null; 694 for (final Map.Entry<TaskProperty,List<Object>> entry : 695 properties.entrySet()) 696 { 697 final TaskProperty p = entry.getKey(); 698 final String attrName = StaticUtils.toLowerCase(p.getAttributeName()); 699 final List<Object> values = entry.getValue(); 700 switch (attrName) 701 { 702 case ATTR_TARGET_DIRECTORY: 703 directory = parseString(p, values, null); 704 break; 705 case ATTR_FILENAME_PATTERN: 706 pattern = parseString(p, values, null); 707 break; 708 case ATTR_TIMESTAMP_FORMAT: 709 final String formatName = parseString(p, values, null); 710 format = FileRetentionTaskTimestampFormat.forName(formatName); 711 break; 712 case ATTR_RETAIN_FILE_COUNT: 713 count = parseLong(p, values, null); 714 break; 715 case ATTR_RETAIN_FILE_AGE: 716 age = parseLong(p, values, null); 717 break; 718 case ATTR_RETAIN_AGGREGATE_FILE_SIZE_BYTES: 719 size = parseLong(p, values, null); 720 break; 721 } 722 } 723 724 targetDirectory = directory; 725 filenamePattern = pattern; 726 timestampFormat = format; 727 retainFileAgeMillis = age; 728 retainAggregateFileSizeBytes = size; 729 730 if (count == null) 731 { 732 retainFileCount = null; 733 } 734 else 735 { 736 retainFileCount = count.intValue(); 737 } 738 739 if ((targetDirectory == null) || targetDirectory.isEmpty()) 740 { 741 throw new TaskException(ERR_FILE_RETENTION_MISSING_REQUIRED_PROPERTY.get( 742 ATTR_TARGET_DIRECTORY)); 743 } 744 745 if ((filenamePattern == null) || filenamePattern.isEmpty()) 746 { 747 throw new TaskException(ERR_FILE_RETENTION_MISSING_REQUIRED_PROPERTY.get( 748 ATTR_FILENAME_PATTERN)); 749 } 750 751 if (timestampFormat == null) 752 { 753 throw new TaskException(ERR_FILE_RETENTION_MISSING_REQUIRED_PROPERTY.get( 754 ATTR_TIMESTAMP_FORMAT)); 755 } 756 757 if ((retainFileCount == null) && (retainFileAgeMillis == null) && 758 (retainAggregateFileSizeBytes == null)) 759 { 760 throw new TaskException(ERR_FILE_RETENTION_MISSING_RETENTION_PROPERTY.get( 761 ATTR_RETAIN_FILE_COUNT, ATTR_RETAIN_FILE_AGE, 762 ATTR_RETAIN_AGGREGATE_FILE_SIZE_BYTES)); 763 } 764 } 765 766 767 768 /** 769 * {@inheritDoc} 770 */ 771 @Override() 772 public String getTaskName() 773 { 774 return INFO_TASK_NAME_FILE_RETENTION.get(); 775 } 776 777 778 779 /** 780 * {@inheritDoc} 781 */ 782 @Override() 783 public String getTaskDescription() 784 { 785 return INFO_TASK_DESCRIPTION_FILE_RETENTION.get(); 786 } 787 788 789 790 /** 791 * Retrieves the path to the directory (on the server filesystem) containing 792 * the files to examine. 793 * 794 * @return The path to the directory (on the server filesystem) containing 795 * the files to examine. 796 */ 797 public String getTargetDirectory() 798 { 799 return targetDirectory; 800 } 801 802 803 804 /** 805 * Retrieves the filename pattern that the task should use to identify which 806 * files to examine. 807 * 808 * @return The filename pattern that the task should use to identify which 809 * files to examine. 810 */ 811 public String getFilenamePattern() 812 { 813 return filenamePattern; 814 } 815 816 817 818 /** 819 * Retrieves the format to use to interpret the timestamp element in the 820 * filename pattern. 821 * 822 * @return The format to use to interpret the timestamp element in the 823 * filename pattern. 824 */ 825 public FileRetentionTaskTimestampFormat getTimestampFormat() 826 { 827 return timestampFormat; 828 } 829 830 831 832 /** 833 * Retrieves the minimum number of files to retain, if defined. 834 * 835 * @return The minimum number of files to retain, or {@code null} if there 836 * is no count-based retention criteria. 837 */ 838 public Integer getRetainFileCount() 839 { 840 return retainFileCount; 841 } 842 843 844 845 /** 846 * Retrieves the minimum age (in milliseconds) of files to retain, if defined. 847 * 848 * @return The minimum age (in milliseconds) of files to retain, or 849 * {@code null} if there is no age-based retention criteria. 850 */ 851 public Long getRetainFileAgeMillis() 852 { 853 return retainFileAgeMillis; 854 } 855 856 857 858 /** 859 * Retrieves the minimum aggregate size (in bytes) of files to retain, if 860 * defined. 861 * 862 * @return The minimum aggregate size (in bytes) of files to retain, or 863 * {@code null} if there is no size-based retention criteria. 864 */ 865 public Long getRetainAggregateFileSizeBytes() 866 { 867 return retainAggregateFileSizeBytes; 868 } 869 870 871 872 /** 873 * {@inheritDoc} 874 */ 875 @Override() 876 protected List<String> getAdditionalObjectClasses() 877 { 878 return Collections.singletonList(OC_FILE_RETENTION_TASK); 879 } 880 881 882 883 /** 884 * {@inheritDoc} 885 */ 886 @Override() 887 protected List<Attribute> getAdditionalAttributes() 888 { 889 final LinkedList<Attribute> attrList = new LinkedList<>(); 890 attrList.add(new Attribute(ATTR_TARGET_DIRECTORY, targetDirectory)); 891 attrList.add(new Attribute(ATTR_FILENAME_PATTERN, filenamePattern)); 892 attrList.add(new Attribute(ATTR_TIMESTAMP_FORMAT, timestampFormat.name())); 893 894 if (retainFileCount != null) 895 { 896 attrList.add(new Attribute(ATTR_RETAIN_FILE_COUNT, 897 String.valueOf(retainFileCount))); 898 } 899 900 if (retainFileAgeMillis != null) 901 { 902 final long retainFileAgeNanos = retainFileAgeMillis * 1_000_000L; 903 final String retainFileAgeString = 904 DurationArgument.nanosToDuration(retainFileAgeNanos); 905 attrList.add(new Attribute(ATTR_RETAIN_FILE_AGE, retainFileAgeString)); 906 } 907 908 if (retainAggregateFileSizeBytes != null) 909 { 910 attrList.add(new Attribute(ATTR_RETAIN_AGGREGATE_FILE_SIZE_BYTES, 911 String.valueOf(retainAggregateFileSizeBytes))); 912 } 913 914 return attrList; 915 } 916 917 918 919 /** 920 * {@inheritDoc} 921 */ 922 @Override() 923 public List<TaskProperty> getTaskSpecificProperties() 924 { 925 return Collections.unmodifiableList(Arrays.asList( 926 PROPERTY_TARGET_DIRECTORY, 927 PROPERTY_FILENAME_PATTERN, 928 PROPERTY_TIMESTAMP_FORMAT, 929 PROPERTY_RETAIN_FILE_COUNT, 930 PROPERTY_RETAIN_FILE_AGE_MILLIS, 931 PROPERTY_RETAIN_AGGREGATE_FILE_SIZE_BYTES)); 932 } 933 934 935 936 /** 937 * {@inheritDoc} 938 */ 939 @Override() 940 public Map<TaskProperty,List<Object>> getTaskPropertyValues() 941 { 942 final LinkedHashMap<TaskProperty, List<Object>> props = 943 new LinkedHashMap<>(StaticUtils.computeMapCapacity(6)); 944 945 props.put(PROPERTY_TARGET_DIRECTORY, 946 Collections.<Object>singletonList(targetDirectory)); 947 props.put(PROPERTY_FILENAME_PATTERN, 948 Collections.<Object>singletonList(filenamePattern)); 949 props.put(PROPERTY_TIMESTAMP_FORMAT, 950 Collections.<Object>singletonList(timestampFormat.name())); 951 952 if (retainFileCount != null) 953 { 954 props.put(PROPERTY_RETAIN_FILE_COUNT, 955 Collections.<Object>singletonList(retainFileCount.longValue())); 956 } 957 958 if (retainFileAgeMillis != null) 959 { 960 props.put(PROPERTY_RETAIN_FILE_AGE_MILLIS, 961 Collections.<Object>singletonList(retainFileAgeMillis)); 962 } 963 964 if (retainAggregateFileSizeBytes != null) 965 { 966 props.put(PROPERTY_RETAIN_AGGREGATE_FILE_SIZE_BYTES, 967 Collections.<Object>singletonList(retainAggregateFileSizeBytes)); 968 } 969 970 return Collections.unmodifiableMap(props); 971 } 972}