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