001/* 002 * Copyright 2008-2020 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2008-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) 2015-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.io.Serializable; 041import java.text.ParseException; 042import java.util.ArrayList; 043import java.util.Arrays; 044import java.util.Collections; 045import java.util.Date; 046import java.util.Iterator; 047import java.util.LinkedHashMap; 048import java.util.List; 049import java.util.Map; 050import java.util.UUID; 051 052import com.unboundid.ldap.sdk.Attribute; 053import com.unboundid.ldap.sdk.Entry; 054import com.unboundid.util.Debug; 055import com.unboundid.util.NotExtensible; 056import com.unboundid.util.StaticUtils; 057import com.unboundid.util.ThreadSafety; 058import com.unboundid.util.ThreadSafetyLevel; 059import com.unboundid.util.Validator; 060 061import static com.unboundid.ldap.sdk.unboundidds.tasks.TaskMessages.*; 062 063 064 065/** 066 * This class defines a data structure for holding information about scheduled 067 * tasks as used by the Ping Identity, UnboundID, or Nokia/Alcatel-Lucent 8661 068 * Directory Server. Subclasses will be used to provide additional 069 * functionality when dealing with certain types of tasks. 070 * <BR> 071 * <BLOCKQUOTE> 072 * <B>NOTE:</B> This class, and other classes within the 073 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 074 * supported for use against Ping Identity, UnboundID, and 075 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 076 * for proprietary functionality or for external specifications that are not 077 * considered stable or mature enough to be guaranteed to work in an 078 * interoperable way with other types of LDAP servers. 079 * </BLOCKQUOTE> 080 * <BR> 081 * All types of tasks can include the following information: 082 * <UL> 083 * <LI>Task ID -- Uniquely identifies the task in the server. It may be 084 * omitted when scheduling a new task in order to have a task ID generated 085 * for the task.</LI> 086 * <LI>Task Class Name -- The fully-qualified name of the {@code Task} 087 * subclass that provides the logic for the task. This does not need to 088 * be provided when creating a new task from one of the task-specific 089 * subclasses.</LI> 090 * <LI>Task State -- The current state of the task. See the {@link TaskState} 091 * enum for information about the possible states that a task may 092 * have.</LI> 093 * <LI>Scheduled Start Time -- The earliest time that the task should be 094 * eligible to start. It may be omitted when scheduling a new task in 095 * order to use the current time.</LI> 096 * <LI>Actual Start Time -- The time that server started processing the 097 * task.</LI> 098 * <LI>Actual Start Time -- The time that server completed processing for the 099 * task.</LI> 100 * <LI>Dependency IDs -- A list of task IDs for tasks that must complete 101 * before this task may be considered eligible to start.</LI> 102 * <LI>Failed Dependency Action -- Specifies how the server should treat this 103 * task if any of the tasks on which it depends failed. See the 104 * {@link FailedDependencyAction} enum for the failed dependency action 105 * values that may be used.</LI> 106 * <LI>Notify on Completion -- A list of e-mail addresses for users that 107 * should be notified when the task completes, regardless of whether it 108 * was successful.</LI> 109 * <LI>Notify On Error -- A list of e-mail addresses for users that should be 110 * notified if the task fails.</LI> 111 * <LI>Log Messages -- A list of the messages logged by the task while it was 112 * running.</LI> 113 * </UL> 114 * Each of these elements can be retrieving using specific methods within this 115 * class (e.g., the {@link Task#getTaskID} method can be used to retrieve the 116 * task ID), but task properties (including those specific to the particular 117 * type to task) may also be accessed using a generic API. For example, the 118 * {@link Task#getTaskPropertyValues} method retrieves a map that correlates the 119 * {@link TaskProperty} objects for the task with the values that have been set 120 * for those properties. See the documentation for the {@link TaskManager} 121 * class for an example that demonstrates accessing task information using the 122 * generic API. 123 * <BR><BR> 124 * Also note that it is possible to create new tasks using information obtained 125 * from the generic API, but that is done on a per-class basis. For example, in 126 * order to create a new {@link BackupTask} instance using the generic API, you 127 * would use the {@link BackupTask#BackupTask(Map)} constructor, in which the 128 * provided map contains a mapping between the properties and their values for 129 * that task. The {@link Task#getTaskSpecificProperties} method may be used to 130 * retrieve a list of the task-specific properties that may be provided when 131 * scheduling a task, and the {@link Task#getCommonTaskProperties} method may be 132 * used to retrieve a list of properties that can be provided when scheduling 133 * any type of task. 134 */ 135@NotExtensible() 136@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 137public class Task 138 implements Serializable 139{ 140 /** 141 * The name of the attribute used to hold the actual start time for scheduled 142 * tasks. 143 */ 144 private static final String ATTR_ACTUAL_START_TIME = 145 "ds-task-actual-start-time"; 146 147 148 149 /** 150 * The name of the attribute used to indicate whether the server should 151 * generate an administrative alert when the task fails to complete 152 * successfully. 153 */ 154 private static final String ATTR_ALERT_ON_ERROR = 155 "ds-task-alert-on-error"; 156 157 158 159 /** 160 * The name of the attribute used to indicate whether the server should 161 * generate an administrative alert when the task starts running. 162 */ 163 private static final String ATTR_ALERT_ON_START = "ds-task-alert-on-start"; 164 165 166 167 /** 168 * The name of the attribute used to indicate whether the server should 169 * generate an administrative alert when the task completes successfully. 170 */ 171 private static final String ATTR_ALERT_ON_SUCCESS = 172 "ds-task-alert-on-success"; 173 174 175 176 /** 177 * The name of the attribute used to hold the completion time for scheduled 178 * tasks. 179 */ 180 private static final String ATTR_COMPLETION_TIME = "ds-task-completion-time"; 181 182 183 184 /** 185 * The name of the attribute used to hold the task IDs for tasks on which a 186 * scheduled task is dependent. 187 */ 188 private static final String ATTR_DEPENDENCY_ID = "ds-task-dependency-id"; 189 190 191 192 /** 193 * The name of the attribute used to indicate what action to take if one of 194 * the dependencies for a task failed to complete successfully. 195 */ 196 private static final String ATTR_FAILED_DEPENDENCY_ACTION = 197 "ds-task-failed-dependency-action"; 198 199 200 201 /** 202 * The name of the attribute used to hold the log messages for scheduled 203 * tasks. 204 */ 205 private static final String ATTR_LOG_MESSAGE = "ds-task-log-message"; 206 207 208 209 /** 210 * The name of the attribute used to hold the e-mail addresses of the users 211 * that should be notified whenever a scheduled task completes, regardless of 212 * success or failure. 213 */ 214 private static final String ATTR_NOTIFY_ON_COMPLETION = 215 "ds-task-notify-on-completion"; 216 217 218 219 /** 220 * The name of the attribute used to hold the e-mail addresses of the users 221 * that should be notified if a scheduled task fails to complete successfully. 222 */ 223 private static final String ATTR_NOTIFY_ON_ERROR = "ds-task-notify-on-error"; 224 225 226 227 /** 228 * The name of the attribute used to hold the e-mail addresses of the users 229 * that should be notified when a scheduled task starts running. 230 */ 231 private static final String ATTR_NOTIFY_ON_START = "ds-task-notify-on-start"; 232 233 234 235 /** 236 * The name of the attribute used to hold the e-mail addresses of the users 237 * that should be notified when a scheduled task completes successfully. 238 */ 239 private static final String ATTR_NOTIFY_ON_SUCCESS = 240 "ds-task-notify-on-success"; 241 242 243 244 /** 245 * The name of the attribute used to hold the scheduled start time for 246 * scheduled tasks. 247 */ 248 private static final String ATTR_SCHEDULED_START_TIME = 249 "ds-task-scheduled-start-time"; 250 251 252 253 /** 254 * The name of the attribute used to hold the name of the class that provides 255 * the logic for scheduled tasks. 256 */ 257 private static final String ATTR_TASK_CLASS = "ds-task-class-name"; 258 259 260 261 /** 262 * The name of the attribute used to hold the task ID for scheduled tasks. 263 */ 264 static final String ATTR_TASK_ID = "ds-task-id"; 265 266 267 268 /** 269 * The name of the attribute used to hold the current state for scheduled 270 * tasks. 271 */ 272 static final String ATTR_TASK_STATE = "ds-task-state"; 273 274 275 276 /** 277 * The name of the base object class for scheduled tasks. 278 */ 279 static final String OC_TASK = "ds-task"; 280 281 282 283 /** 284 * The DN of the entry below which scheduled tasks reside. 285 */ 286 static final String SCHEDULED_TASKS_BASE_DN = 287 "cn=Scheduled Tasks,cn=tasks"; 288 289 290 291 /** 292 * The task property that will be used for the task ID. 293 */ 294 private static final TaskProperty PROPERTY_TASK_ID = 295 new TaskProperty(ATTR_TASK_ID, INFO_DISPLAY_NAME_TASK_ID.get(), 296 INFO_DESCRIPTION_TASK_ID.get(), String.class, false, 297 false, true); 298 299 300 301 /** 302 * The task property that will be used for the scheduled start time. 303 */ 304 private static final TaskProperty PROPERTY_SCHEDULED_START_TIME = 305 new TaskProperty(ATTR_SCHEDULED_START_TIME, 306 INFO_DISPLAY_NAME_SCHEDULED_START_TIME.get(), 307 INFO_DESCRIPTION_SCHEDULED_START_TIME.get(), Date.class, 308 false, false, true); 309 310 311 312 /** 313 * The task property that will be used for the set of dependency IDs. 314 */ 315 private static final TaskProperty PROPERTY_DEPENDENCY_ID = 316 new TaskProperty(ATTR_DEPENDENCY_ID, 317 INFO_DISPLAY_NAME_DEPENDENCY_ID.get(), 318 INFO_DESCRIPTION_DEPENDENCY_ID.get(), String.class, 319 false, true, true); 320 321 322 323 /** 324 * The task property that will be used for the failed dependency action. 325 */ 326 private static final TaskProperty PROPERTY_FAILED_DEPENDENCY_ACTION = 327 new TaskProperty(ATTR_FAILED_DEPENDENCY_ACTION, 328 INFO_DISPLAY_NAME_FAILED_DEPENDENCY_ACTION.get(), 329 INFO_DESCRIPTION_FAILED_DEPENDENCY_ACTION.get(), 330 String.class, false, false, true, 331 new String[] 332 { 333 FailedDependencyAction.CANCEL.getName(), 334 FailedDependencyAction.DISABLE.getName(), 335 FailedDependencyAction.PROCESS.getName() 336 }); 337 338 339 340 /** 341 * The task property that will be used for the notify on completion addresses. 342 */ 343 private static final TaskProperty PROPERTY_NOTIFY_ON_COMPLETION = 344 new TaskProperty(ATTR_NOTIFY_ON_COMPLETION, 345 INFO_DISPLAY_NAME_NOTIFY_ON_COMPLETION.get(), 346 INFO_DESCRIPTION_NOTIFY_ON_COMPLETION.get(), 347 String.class, false, true, true); 348 349 350 351 /** 352 * The task property that will be used for the notify on error addresses. 353 */ 354 private static final TaskProperty PROPERTY_NOTIFY_ON_ERROR = 355 new TaskProperty(ATTR_NOTIFY_ON_ERROR, 356 INFO_DISPLAY_NAME_NOTIFY_ON_ERROR.get(), 357 INFO_DESCRIPTION_NOTIFY_ON_ERROR.get(), 358 String.class, false, true, true); 359 360 361 362 /** 363 * The task property that will be used for the notify on success addresses. 364 */ 365 private static final TaskProperty PROPERTY_NOTIFY_ON_SUCCESS = 366 new TaskProperty(ATTR_NOTIFY_ON_SUCCESS, 367 INFO_DISPLAY_NAME_NOTIFY_ON_SUCCESS.get(), 368 INFO_DESCRIPTION_NOTIFY_ON_SUCCESS.get(), 369 String.class, false, true, true); 370 371 372 373 /** 374 * The task property that will be used for the notify on start addresses. 375 */ 376 private static final TaskProperty PROPERTY_NOTIFY_ON_START = 377 new TaskProperty(ATTR_NOTIFY_ON_START, 378 INFO_DISPLAY_NAME_NOTIFY_ON_START.get(), 379 INFO_DESCRIPTION_NOTIFY_ON_START.get(), 380 String.class, false, true, true); 381 382 383 384 /** 385 * The task property that will be used for the alert on error flag. 386 */ 387 private static final TaskProperty PROPERTY_ALERT_ON_ERROR = 388 new TaskProperty(ATTR_ALERT_ON_ERROR, 389 INFO_DISPLAY_NAME_ALERT_ON_ERROR.get(), 390 INFO_DESCRIPTION_ALERT_ON_ERROR.get(), 391 Boolean.class, false, false, true); 392 393 394 395 /** 396 * The task property that will be used for the alert on start flag. 397 */ 398 private static final TaskProperty PROPERTY_ALERT_ON_START = 399 new TaskProperty(ATTR_ALERT_ON_START, 400 INFO_DISPLAY_NAME_ALERT_ON_START.get(), 401 INFO_DESCRIPTION_ALERT_ON_START.get(), 402 Boolean.class, false, false, true); 403 404 405 406 /** 407 * The task property that will be used for the alert on success flag. 408 */ 409 private static final TaskProperty PROPERTY_ALERT_ON_SUCCESS = 410 new TaskProperty(ATTR_ALERT_ON_SUCCESS, 411 INFO_DISPLAY_NAME_ALERT_ON_SUCCESS.get(), 412 INFO_DESCRIPTION_ALERT_ON_SUCCESS.get(), 413 Boolean.class, false, false, true); 414 415 416 417 /** 418 * The serial version UID for this serializable class. 419 */ 420 private static final long serialVersionUID = -4082350090081577623L; 421 422 423 424 // Indicates whether to generate an administrative alert when the task fails 425 // to complete successfully. 426 private final Boolean alertOnError; 427 428 // Indicates whether to generate an administrative alert when the task starts. 429 private final Boolean alertOnStart; 430 431 // Indicates whether to generate an administrative alert when the task 432 // completes successfully. 433 private final Boolean alertOnSuccess; 434 435 // The time that this task actually started. 436 private final Date actualStartTime; 437 438 // The time that this task completed. 439 private final Date completionTime; 440 441 // The time that this task was scheduled to start. 442 private final Date scheduledStartTime; 443 444 // The entry from which this task was decoded. 445 private final Entry taskEntry; 446 447 // The failed dependency action for this task. 448 private final FailedDependencyAction failedDependencyAction; 449 450 // The set of task IDs of the tasks on which this task is dependent. 451 private final List<String> dependencyIDs; 452 453 // The set of log messages for this task. 454 private final List<String> logMessages; 455 456 // The set of e-mail addresses of users that should be notified when the task 457 // processing is complete. 458 private final List<String> notifyOnCompletion; 459 460 // The set of e-mail addresses of users that should be notified if task 461 // processing completes with an error. 462 private final List<String> notifyOnError; 463 464 // The set of e-mail addresses of users that should be notified if task 465 // processing starts. 466 private final List<String> notifyOnStart; 467 468 // The set of e-mail addresses of users that should be notified if task 469 // processing completes successfully. 470 private final List<String> notifyOnSuccess; 471 472 // The fully-qualified name of the task class. 473 private final String taskClassName; 474 475 // The DN of the entry for this task. 476 private final String taskEntryDN; 477 478 // The task ID for this task. 479 private final String taskID; 480 481 // The current state for this task. 482 private final TaskState taskState; 483 484 485 486 /** 487 * Creates a new uninitialized task instance which should only be used for 488 * obtaining general information about this task, including the task name, 489 * description, and supported properties. Attempts to use a task created with 490 * this constructor for any other reason will likely fail. 491 */ 492 protected Task() 493 { 494 alertOnError = null; 495 alertOnStart = null; 496 alertOnSuccess = null; 497 actualStartTime = null; 498 completionTime = null; 499 scheduledStartTime = null; 500 taskEntry = null; 501 failedDependencyAction = null; 502 dependencyIDs = null; 503 logMessages = null; 504 notifyOnCompletion = null; 505 notifyOnError = null; 506 notifyOnStart = null; 507 notifyOnSuccess = null; 508 taskClassName = null; 509 taskEntryDN = null; 510 taskID = null; 511 taskState = null; 512 } 513 514 515 516 /** 517 * Creates a new unscheduled task with the specified task ID and class name. 518 * 519 * @param taskID The task ID to use for this task. If it is 520 * {@code null} then a UUID will be generated for use 521 * as the task ID. 522 * @param taskClassName The fully-qualified name of the Java class that 523 * provides the logic for the task. It must not be 524 * {@code null}. 525 */ 526 public Task(final String taskID, final String taskClassName) 527 { 528 this(taskID, taskClassName, null, null, null, null, null); 529 } 530 531 532 533 /** 534 * Creates a new unscheduled task with the provided information. 535 * 536 * @param taskID The task ID to use for this task. 537 * @param taskClassName The fully-qualified name of the Java class 538 * that provides the logic for the task. It 539 * must not be {@code null}. 540 * @param scheduledStartTime The time that this task should start 541 * running. 542 * @param dependencyIDs The list of task IDs that will be required 543 * to complete before this task will be 544 * eligible to start. 545 * @param failedDependencyAction Indicates what action should be taken if 546 * any of the dependencies for this task do 547 * not complete successfully. 548 * @param notifyOnCompletion The list of e-mail addresses of individuals 549 * that should be notified when this task 550 * completes. 551 * @param notifyOnError The list of e-mail addresses of individuals 552 * that should be notified if this task does 553 * not complete successfully. 554 */ 555 public Task(final String taskID, final String taskClassName, 556 final Date scheduledStartTime, final List<String> dependencyIDs, 557 final FailedDependencyAction failedDependencyAction, 558 final List<String> notifyOnCompletion, 559 final List<String> notifyOnError) 560 { 561 this(taskID, taskClassName, scheduledStartTime, dependencyIDs, 562 failedDependencyAction, null, notifyOnCompletion, null, 563 notifyOnError, null, null, null); 564 } 565 566 567 568 /** 569 * Creates a new unscheduled task with the provided information. 570 * 571 * @param taskID The task ID to use for this task. 572 * @param taskClassName The fully-qualified name of the Java class 573 * that provides the logic for the task. It 574 * must not be {@code null}. 575 * @param scheduledStartTime The time that this task should start 576 * running. 577 * @param dependencyIDs The list of task IDs that will be required 578 * to complete before this task will be 579 * eligible to start. 580 * @param failedDependencyAction Indicates what action should be taken if 581 * any of the dependencies for this task do 582 * not complete successfully. 583 * @param notifyOnStart The list of e-mail addresses of individuals 584 * that should be notified when this task 585 * starts running. 586 * @param notifyOnCompletion The list of e-mail addresses of individuals 587 * that should be notified when this task 588 * completes. 589 * @param notifyOnSuccess The list of e-mail addresses of individuals 590 * that should be notified if this task 591 * completes successfully. 592 * @param notifyOnError The list of e-mail addresses of individuals 593 * that should be notified if this task does 594 * not complete successfully. 595 * @param alertOnStart Indicates whether the server should send an 596 * alert notification when this task starts. 597 * @param alertOnSuccess Indicates whether the server should send an 598 * alert notification if this task completes 599 * successfully. 600 * @param alertOnError Indicates whether the server should send an 601 * alert notification if this task fails to 602 * complete successfully. 603 */ 604 public Task(final String taskID, final String taskClassName, 605 final Date scheduledStartTime, final List<String> dependencyIDs, 606 final FailedDependencyAction failedDependencyAction, 607 final List<String> notifyOnStart, 608 final List<String> notifyOnCompletion, 609 final List<String> notifyOnSuccess, 610 final List<String> notifyOnError, final Boolean alertOnStart, 611 final Boolean alertOnSuccess, final Boolean alertOnError) 612 { 613 Validator.ensureNotNull(taskClassName); 614 615 this.taskClassName = taskClassName; 616 this.scheduledStartTime = scheduledStartTime; 617 this.failedDependencyAction = failedDependencyAction; 618 this.alertOnStart = alertOnStart; 619 this.alertOnSuccess = alertOnSuccess; 620 this.alertOnError = alertOnError; 621 622 if (taskID == null) 623 { 624 this.taskID = UUID.randomUUID().toString(); 625 } 626 else 627 { 628 this.taskID = taskID; 629 } 630 631 if (dependencyIDs == null) 632 { 633 this.dependencyIDs = Collections.emptyList(); 634 } 635 else 636 { 637 this.dependencyIDs = Collections.unmodifiableList(dependencyIDs); 638 } 639 640 if (notifyOnStart == null) 641 { 642 this.notifyOnStart = Collections.emptyList(); 643 } 644 else 645 { 646 this.notifyOnStart = 647 Collections.unmodifiableList(notifyOnStart); 648 } 649 650 if (notifyOnCompletion == null) 651 { 652 this.notifyOnCompletion = Collections.emptyList(); 653 } 654 else 655 { 656 this.notifyOnCompletion = 657 Collections.unmodifiableList(notifyOnCompletion); 658 } 659 660 if (notifyOnSuccess == null) 661 { 662 this.notifyOnSuccess = Collections.emptyList(); 663 } 664 else 665 { 666 this.notifyOnSuccess = Collections.unmodifiableList(notifyOnSuccess); 667 } 668 669 if (notifyOnError == null) 670 { 671 this.notifyOnError = Collections.emptyList(); 672 } 673 else 674 { 675 this.notifyOnError = Collections.unmodifiableList(notifyOnError); 676 } 677 678 taskEntry = null; 679 taskEntryDN = ATTR_TASK_ID + '=' + this.taskID + ',' + 680 SCHEDULED_TASKS_BASE_DN; 681 actualStartTime = null; 682 completionTime = null; 683 logMessages = Collections.emptyList(); 684 taskState = TaskState.UNSCHEDULED; 685 } 686 687 688 689 /** 690 * Creates a new task from the provided entry. 691 * 692 * @param entry The entry to use to create this task. 693 * 694 * @throws TaskException If the provided entry cannot be parsed as a 695 * scheduled task. 696 */ 697 public Task(final Entry entry) 698 throws TaskException 699 { 700 taskEntry = entry; 701 taskEntryDN = entry.getDN(); 702 703 // Ensure that the task entry has the appropriate object class for a 704 // scheduled task. 705 if (! entry.hasObjectClass(OC_TASK)) 706 { 707 throw new TaskException(ERR_TASK_MISSING_OC.get(taskEntryDN)); 708 } 709 710 711 // Get the task ID. It must be present. 712 taskID = entry.getAttributeValue(ATTR_TASK_ID); 713 if (taskID == null) 714 { 715 throw new TaskException(ERR_TASK_NO_ID.get(taskEntryDN)); 716 } 717 718 719 // Get the task class name. It must be present. 720 taskClassName = entry.getAttributeValue(ATTR_TASK_CLASS); 721 if (taskClassName == null) 722 { 723 throw new TaskException(ERR_TASK_NO_CLASS.get(taskEntryDN)); 724 } 725 726 727 // Get the task state. If it is not present, then assume "unscheduled". 728 final String stateStr = entry.getAttributeValue(ATTR_TASK_STATE); 729 if (stateStr == null) 730 { 731 taskState = TaskState.UNSCHEDULED; 732 } 733 else 734 { 735 taskState = TaskState.forName(stateStr); 736 if (taskState == null) 737 { 738 throw new TaskException(ERR_TASK_INVALID_STATE.get(taskEntryDN, 739 stateStr)); 740 } 741 } 742 743 744 // Get the scheduled start time. It may be absent. 745 String timestamp = entry.getAttributeValue(ATTR_SCHEDULED_START_TIME); 746 if (timestamp == null) 747 { 748 scheduledStartTime = null; 749 } 750 else 751 { 752 try 753 { 754 scheduledStartTime = StaticUtils.decodeGeneralizedTime(timestamp); 755 } 756 catch (final ParseException pe) 757 { 758 Debug.debugException(pe); 759 throw new TaskException(ERR_TASK_CANNOT_PARSE_SCHEDULED_START_TIME.get( 760 taskEntryDN, timestamp, pe.getMessage()), 761 pe); 762 } 763 } 764 765 766 // Get the actual start time. It may be absent. 767 timestamp = entry.getAttributeValue(ATTR_ACTUAL_START_TIME); 768 if (timestamp == null) 769 { 770 actualStartTime = null; 771 } 772 else 773 { 774 try 775 { 776 actualStartTime = StaticUtils.decodeGeneralizedTime(timestamp); 777 } 778 catch (final ParseException pe) 779 { 780 Debug.debugException(pe); 781 throw new TaskException(ERR_TASK_CANNOT_PARSE_ACTUAL_START_TIME.get( 782 taskEntryDN, timestamp, pe.getMessage()), 783 pe); 784 } 785 } 786 787 788 // Get the completion start time. It may be absent. 789 timestamp = entry.getAttributeValue(ATTR_COMPLETION_TIME); 790 if (timestamp == null) 791 { 792 completionTime = null; 793 } 794 else 795 { 796 try 797 { 798 completionTime = StaticUtils.decodeGeneralizedTime(timestamp); 799 } 800 catch (final ParseException pe) 801 { 802 Debug.debugException(pe); 803 throw new TaskException(ERR_TASK_CANNOT_PARSE_COMPLETION_TIME.get( 804 taskEntryDN, timestamp, pe.getMessage()), 805 pe); 806 } 807 } 808 809 810 // Get the failed dependency action for this task. It may be absent. 811 final String name = entry.getAttributeValue(ATTR_FAILED_DEPENDENCY_ACTION); 812 if (name == null) 813 { 814 failedDependencyAction = null; 815 } 816 else 817 { 818 failedDependencyAction = FailedDependencyAction.forName(name); 819 } 820 821 822 // Get the dependent task IDs for this task. It may be absent. 823 dependencyIDs = parseStringList(entry, ATTR_DEPENDENCY_ID); 824 825 826 // Get the log messages for this task. It may be absent. 827 logMessages = parseStringList(entry, ATTR_LOG_MESSAGE); 828 829 830 // Get the notify on start addresses for this task. It may be absent. 831 notifyOnStart = parseStringList(entry, ATTR_NOTIFY_ON_START); 832 833 834 // Get the notify on completion addresses for this task. It may be absent. 835 notifyOnCompletion = parseStringList(entry, ATTR_NOTIFY_ON_COMPLETION); 836 837 838 // Get the notify on success addresses for this task. It may be absent. 839 notifyOnSuccess = parseStringList(entry, ATTR_NOTIFY_ON_SUCCESS); 840 841 842 // Get the notify on error addresses for this task. It may be absent. 843 notifyOnError = parseStringList(entry, ATTR_NOTIFY_ON_ERROR); 844 845 846 // Get the alert on start flag for this task. It may be absent. 847 alertOnStart = entry.getAttributeValueAsBoolean(ATTR_ALERT_ON_START); 848 849 850 // Get the alert on success flag for this task. It may be absent. 851 alertOnSuccess = entry.getAttributeValueAsBoolean(ATTR_ALERT_ON_SUCCESS); 852 853 854 // Get the alert on error flag for this task. It may be absent. 855 alertOnError = entry.getAttributeValueAsBoolean(ATTR_ALERT_ON_ERROR); 856 } 857 858 859 860 /** 861 * Creates a new task from the provided set of task properties. 862 * 863 * @param taskClassName The fully-qualified name of the Java class that 864 * provides the logic for the task. It must not be 865 * {@code null}. 866 * @param properties The set of task properties and their corresponding 867 * values to use for the task. It must not be 868 * {@code null}. 869 * 870 * @throws TaskException If the provided set of properties cannot be used to 871 * create a valid scheduled task. 872 */ 873 public Task(final String taskClassName, 874 final Map<TaskProperty,List<Object>> properties) 875 throws TaskException 876 { 877 Validator.ensureNotNull(taskClassName, properties); 878 879 this.taskClassName = taskClassName; 880 881 String idStr = UUID.randomUUID().toString(); 882 Date sst = null; 883 String[] depIDs = StaticUtils.NO_STRINGS; 884 FailedDependencyAction fda = FailedDependencyAction.CANCEL; 885 String[] nob = StaticUtils.NO_STRINGS; 886 String[] noc = StaticUtils.NO_STRINGS; 887 String[] noe = StaticUtils.NO_STRINGS; 888 String[] nos = StaticUtils.NO_STRINGS; 889 Boolean aob = null; 890 Boolean aoe = null; 891 Boolean aos = null; 892 893 for (final Map.Entry<TaskProperty,List<Object>> entry : 894 properties.entrySet()) 895 { 896 final TaskProperty p = entry.getKey(); 897 final String attrName = p.getAttributeName(); 898 final List<Object> values = entry.getValue(); 899 900 if (attrName.equalsIgnoreCase(ATTR_TASK_ID)) 901 { 902 idStr = parseString(p, values, idStr); 903 } 904 else if (attrName.equalsIgnoreCase(ATTR_SCHEDULED_START_TIME)) 905 { 906 sst = parseDate(p, values, sst); 907 } 908 else if (attrName.equalsIgnoreCase(ATTR_DEPENDENCY_ID)) 909 { 910 depIDs = parseStrings(p, values, depIDs); 911 } 912 else if (attrName.equalsIgnoreCase(ATTR_FAILED_DEPENDENCY_ACTION)) 913 { 914 fda = FailedDependencyAction.forName( 915 parseString(p, values, fda.getName())); 916 } 917 else if (attrName.equalsIgnoreCase(ATTR_NOTIFY_ON_START)) 918 { 919 nob = parseStrings(p, values, nob); 920 } 921 else if (attrName.equalsIgnoreCase(ATTR_NOTIFY_ON_COMPLETION)) 922 { 923 noc = parseStrings(p, values, noc); 924 } 925 else if (attrName.equalsIgnoreCase(ATTR_NOTIFY_ON_SUCCESS)) 926 { 927 nos = parseStrings(p, values, nos); 928 } 929 else if (attrName.equalsIgnoreCase(ATTR_NOTIFY_ON_ERROR)) 930 { 931 noe = parseStrings(p, values, noe); 932 } 933 else if (attrName.equalsIgnoreCase(ATTR_ALERT_ON_START)) 934 { 935 aob = parseBoolean(p, values, aob); 936 } 937 else if (attrName.equalsIgnoreCase(ATTR_ALERT_ON_SUCCESS)) 938 { 939 aos = parseBoolean(p, values, aos); 940 } 941 else if (attrName.equalsIgnoreCase(ATTR_ALERT_ON_ERROR)) 942 { 943 aoe = parseBoolean(p, values, aoe); 944 } 945 } 946 947 taskID = idStr; 948 scheduledStartTime = sst; 949 dependencyIDs = Collections.unmodifiableList(Arrays.asList(depIDs)); 950 failedDependencyAction = fda; 951 notifyOnStart = Collections.unmodifiableList(Arrays.asList(nob)); 952 notifyOnCompletion = Collections.unmodifiableList(Arrays.asList(noc)); 953 notifyOnSuccess = Collections.unmodifiableList(Arrays.asList(nos)); 954 notifyOnError = Collections.unmodifiableList(Arrays.asList(noe)); 955 alertOnStart = aob; 956 alertOnSuccess = aos; 957 alertOnError = aoe; 958 taskEntry = null; 959 taskEntryDN = ATTR_TASK_ID + '=' + taskID + ',' + SCHEDULED_TASKS_BASE_DN; 960 actualStartTime = null; 961 completionTime = null; 962 logMessages = Collections.emptyList(); 963 taskState = TaskState.UNSCHEDULED; 964 } 965 966 967 968 /** 969 * Retrieves a list containing instances of the available task types. The 970 * provided task instances will may only be used for obtaining general 971 * information about the task (e.g., name, description, and supported 972 * properties). 973 * 974 * @return A list containing instances of the available task types. 975 */ 976 public static List<Task> getAvailableTaskTypes() 977 { 978 final List<Task> taskList = Arrays.asList( 979 new AddSchemaFileTask(), 980 new AlertTask(), 981 new AuditDataSecurityTask(), 982 new BackupTask(), 983 new CollectSupportDataTask(), 984 new DelayTask(), 985 new DisconnectClientTask(), 986 new DumpDBDetailsTask(), 987 new EnterLockdownModeTask(), 988 new ExecTask(), 989 new ExportTask(), 990 new FileRetentionTask(), 991 new GroovyScriptedTask(), 992 new ImportTask(), 993 new LeaveLockdownModeTask(), 994 new RebuildTask(), 995 new ReEncodeEntriesTask(), 996 new RefreshEncryptionSettingsTask(), 997 new ReloadGlobalIndexTask(), 998 new ReloadHTTPConnectionHandlerCertificatesTask(), 999 new RestoreTask(), 1000 new RotateLogTask(), 1001 new SearchTask(), 1002 new ShutdownTask(), 1003 new SynchronizeEncryptionSettingsTask(), 1004 new ThirdPartyTask()); 1005 1006 return Collections.unmodifiableList(taskList); 1007 } 1008 1009 1010 1011 /** 1012 * Retrieves a human-readable name for this task. 1013 * 1014 * @return A human-readable name for this task. 1015 */ 1016 public String getTaskName() 1017 { 1018 return INFO_TASK_NAME_GENERIC.get(); 1019 } 1020 1021 1022 1023 /** 1024 * Retrieves a human-readable description for this task. 1025 * 1026 * @return A human-readable description for this task. 1027 */ 1028 public String getTaskDescription() 1029 { 1030 return INFO_TASK_DESCRIPTION_GENERIC.get(); 1031 } 1032 1033 1034 1035 /** 1036 * Retrieves the entry from which this task was decoded, if available. Note 1037 * that although the entry is not immutable, changes made to it will not be 1038 * reflected in this task. 1039 * 1040 * @return The entry from which this task was decoded, or {@code null} if 1041 * this task was not created from an existing entry. 1042 */ 1043 protected final Entry getTaskEntry() 1044 { 1045 return taskEntry; 1046 } 1047 1048 1049 1050 /** 1051 * Retrieves the DN of the entry in which this scheduled task is defined. 1052 * 1053 * @return The DN of the entry in which this scheduled task is defined. 1054 */ 1055 public final String getTaskEntryDN() 1056 { 1057 return taskEntryDN; 1058 } 1059 1060 1061 1062 /** 1063 * Retrieves the task ID for this task. 1064 * 1065 * @return The task ID for this task. 1066 */ 1067 public final String getTaskID() 1068 { 1069 return taskID; 1070 } 1071 1072 1073 1074 /** 1075 * Retrieves the fully-qualified name of the Java class that provides the 1076 * logic for this class. 1077 * 1078 * @return The fully-qualified name of the Java class that provides the logic 1079 * for this task. 1080 */ 1081 public final String getTaskClassName() 1082 { 1083 return taskClassName; 1084 } 1085 1086 1087 1088 /** 1089 * Retrieves the current state for this task. 1090 * 1091 * @return The current state for this task. 1092 */ 1093 public final TaskState getState() 1094 { 1095 return taskState; 1096 } 1097 1098 1099 1100 /** 1101 * Indicates whether this task is currently pending execution. 1102 * 1103 * @return {@code true} if this task is currently pending execution, or 1104 * {@code false} if not. 1105 */ 1106 public final boolean isPending() 1107 { 1108 return taskState.isPending(); 1109 } 1110 1111 1112 1113 /** 1114 * Indicates whether this task is currently running. 1115 * 1116 * @return {@code true} if this task is currently running, or {@code false} 1117 * if not. 1118 */ 1119 public final boolean isRunning() 1120 { 1121 return taskState.isRunning(); 1122 } 1123 1124 1125 1126 /** 1127 * Indicates whether this task has completed execution. 1128 * 1129 * @return {@code true} if this task has completed execution, or 1130 * {@code false} if not. 1131 */ 1132 public final boolean isCompleted() 1133 { 1134 return taskState.isCompleted(); 1135 } 1136 1137 1138 1139 /** 1140 * Retrieves the time that this task is/was scheduled to start running. 1141 * 1142 * @return The time that this task is/was scheduled to start running, or 1143 * {@code null} if that is not available and therefore the task 1144 * should start running as soon as all dependencies have been met. 1145 */ 1146 public final Date getScheduledStartTime() 1147 { 1148 return scheduledStartTime; 1149 } 1150 1151 1152 1153 /** 1154 * Retrieves the time that this task actually started running. 1155 * 1156 * @return The time that this task actually started running, or {@code null} 1157 * if that is not available (e.g., because the task has not yet 1158 * started). 1159 */ 1160 public final Date getActualStartTime() 1161 { 1162 return actualStartTime; 1163 } 1164 1165 1166 1167 /** 1168 * Retrieves the time that this task completed. 1169 * 1170 * @return The time that this task completed, or {@code null} if it has not 1171 * yet completed. 1172 */ 1173 public final Date getCompletionTime() 1174 { 1175 return completionTime; 1176 } 1177 1178 1179 1180 /** 1181 * Retrieves a list of the task IDs for tasks that must complete before this 1182 * task will be eligible to start. 1183 * 1184 * @return A list of the task IDs for tasks that must complete before this 1185 * task will be eligible to start, or an empty list if this task does 1186 * not have any dependencies. 1187 */ 1188 public final List<String> getDependencyIDs() 1189 { 1190 return dependencyIDs; 1191 } 1192 1193 1194 1195 /** 1196 * Retrieves the failed dependency action for this task, which indicates the 1197 * behavior that it should exhibit if any of its dependencies encounter a 1198 * failure. 1199 * 1200 * @return The failed dependency action for this task, or {@code null} if it 1201 * is not available. 1202 */ 1203 public final FailedDependencyAction getFailedDependencyAction() 1204 { 1205 return failedDependencyAction; 1206 } 1207 1208 1209 1210 /** 1211 * Retrieves the log messages for this task. Note that if the task has 1212 * generated a very large number of log messages, then only a portion of the 1213 * most recent messages may be available. 1214 * 1215 * @return The log messages for this task, or an empty list if this task does 1216 * not have any log messages. 1217 */ 1218 public final List<String> getLogMessages() 1219 { 1220 return logMessages; 1221 } 1222 1223 1224 1225 /** 1226 * Retrieves a list of the e-mail addresses of the individuals that should be 1227 * notified whenever this task starts running. 1228 * 1229 * @return A list of the e-mail addresses of the individuals that should be 1230 * notified whenever this task starts running, or an empty list if 1231 * there are none. 1232 */ 1233 public final List<String> getNotifyOnStartAddresses() 1234 { 1235 return notifyOnStart; 1236 } 1237 1238 1239 1240 /** 1241 * Retrieves a list of the e-mail addresses of the individuals that should be 1242 * notified whenever this task completes processing, regardless of whether it 1243 * was successful. 1244 * 1245 * @return A list of the e-mail addresses of the individuals that should be 1246 * notified whenever this task completes processing, or an empty list 1247 * if there are none. 1248 */ 1249 public final List<String> getNotifyOnCompletionAddresses() 1250 { 1251 return notifyOnCompletion; 1252 } 1253 1254 1255 1256 /** 1257 * Retrieves a list of the e-mail addresses of the individuals that should be 1258 * notified if this task completes successfully. 1259 * 1260 * @return A list of the e-mail addresses of the individuals that should be 1261 * notified if this task completes successfully, or an empty list 1262 * if there are none. 1263 */ 1264 public final List<String> getNotifyOnSuccessAddresses() 1265 { 1266 return notifyOnSuccess; 1267 } 1268 1269 1270 1271 /** 1272 * Retrieves a list of the e-mail addresses of the individuals that should be 1273 * notified if this task stops processing prematurely due to an error or 1274 * other external action (e.g., server shutdown or administrative cancel). 1275 * 1276 * @return A list of the e-mail addresses of the individuals that should be 1277 * notified if this task stops processing prematurely, or an empty 1278 * list if there are none. 1279 */ 1280 public final List<String> getNotifyOnErrorAddresses() 1281 { 1282 return notifyOnError; 1283 } 1284 1285 1286 1287 /** 1288 * Retrieves the flag that indicates whether the server should generate an 1289 * administrative alert when this task starts running. 1290 * 1291 * @return {@code true} if the server should send an alert when this task 1292 * starts running, {@code false} if the server should not send an 1293 * alert, or {@code null} if it is not available. 1294 */ 1295 public final Boolean getAlertOnStart() 1296 { 1297 return alertOnStart; 1298 } 1299 1300 1301 1302 /** 1303 * Retrieves the flag that indicates whether the server should generate an 1304 * administrative alert if this task completes successfully. 1305 * 1306 * @return {@code true} if the server should send an alert if this task 1307 * completes successfully, {@code false} if the server should not 1308 * send an alert, or {@code null} if it is not available. 1309 */ 1310 public final Boolean getAlertOnSuccess() 1311 { 1312 return alertOnSuccess; 1313 } 1314 1315 1316 1317 /** 1318 * Retrieves the flag that indicates whether the server should generate an 1319 * administrative alert if this task fails to complete successfully. 1320 * 1321 * @return {@code true} if the server should send an alert if this task fails 1322 * to complete successfully, {@code false} if the server should not 1323 * send an alert, or {@code null} if it is not available. 1324 */ 1325 public final Boolean getAlertOnError() 1326 { 1327 return alertOnError; 1328 } 1329 1330 1331 1332 /** 1333 * Creates an entry that may be added to the Directory Server to create a new 1334 * instance of this task. 1335 * 1336 * @return An entry that may be added to the Directory Server to create a new 1337 * instance of this task. 1338 */ 1339 public final Entry createTaskEntry() 1340 { 1341 final ArrayList<Attribute> attributes = new ArrayList<>(20); 1342 1343 final ArrayList<String> ocValues = new ArrayList<>(5); 1344 ocValues.add("top"); 1345 ocValues.add(OC_TASK); 1346 ocValues.addAll(getAdditionalObjectClasses()); 1347 attributes.add(new Attribute("objectClass", ocValues)); 1348 1349 attributes.add(new Attribute(ATTR_TASK_ID, taskID)); 1350 1351 attributes.add(new Attribute(ATTR_TASK_CLASS, taskClassName)); 1352 1353 if (scheduledStartTime != null) 1354 { 1355 attributes.add(new Attribute(ATTR_SCHEDULED_START_TIME, 1356 StaticUtils.encodeGeneralizedTime(scheduledStartTime))); 1357 } 1358 1359 if (! dependencyIDs.isEmpty()) 1360 { 1361 attributes.add(new Attribute(ATTR_DEPENDENCY_ID, dependencyIDs)); 1362 } 1363 1364 if (failedDependencyAction != null) 1365 { 1366 attributes.add(new Attribute(ATTR_FAILED_DEPENDENCY_ACTION, 1367 failedDependencyAction.getName())); 1368 } 1369 1370 if (! notifyOnStart.isEmpty()) 1371 { 1372 attributes.add(new Attribute(ATTR_NOTIFY_ON_START, 1373 notifyOnStart)); 1374 } 1375 1376 if (! notifyOnCompletion.isEmpty()) 1377 { 1378 attributes.add(new Attribute(ATTR_NOTIFY_ON_COMPLETION, 1379 notifyOnCompletion)); 1380 } 1381 1382 if (! notifyOnSuccess.isEmpty()) 1383 { 1384 attributes.add(new Attribute(ATTR_NOTIFY_ON_SUCCESS, notifyOnSuccess)); 1385 } 1386 1387 if (! notifyOnError.isEmpty()) 1388 { 1389 attributes.add(new Attribute(ATTR_NOTIFY_ON_ERROR, notifyOnError)); 1390 } 1391 1392 if (alertOnStart != null) 1393 { 1394 attributes.add(new Attribute(ATTR_ALERT_ON_START, 1395 String.valueOf(alertOnStart))); 1396 } 1397 1398 if (alertOnSuccess != null) 1399 { 1400 attributes.add(new Attribute(ATTR_ALERT_ON_SUCCESS, 1401 String.valueOf(alertOnSuccess))); 1402 } 1403 1404 if (alertOnError != null) 1405 { 1406 attributes.add(new Attribute(ATTR_ALERT_ON_ERROR, 1407 String.valueOf(alertOnError))); 1408 } 1409 1410 attributes.addAll(getAdditionalAttributes()); 1411 1412 return new Entry(taskEntryDN, attributes); 1413 } 1414 1415 1416 1417 /** 1418 * Parses the value of the specified attribute as a {@code boolean} value, or 1419 * throws an exception if the value cannot be decoded as a boolean. 1420 * 1421 * @param taskEntry The entry containing the attribute to be parsed. 1422 * @param attributeName The name of the attribute from which the value was 1423 * taken. 1424 * @param defaultValue The default value to use if the provided value 1425 * string is {@code null}. 1426 * 1427 * @return {@code true} if the value string represents a boolean value of 1428 * {@code true}, {@code false} if the value string represents a 1429 * boolean value of {@code false}, or the default value if the value 1430 * string is {@code null}. 1431 * 1432 * @throws TaskException If the provided value string cannot be parsed as a 1433 * {@code boolean} value. 1434 */ 1435 protected static boolean parseBooleanValue(final Entry taskEntry, 1436 final String attributeName, 1437 final boolean defaultValue) 1438 throws TaskException 1439 { 1440 final String valueString = taskEntry.getAttributeValue(attributeName); 1441 if (valueString == null) 1442 { 1443 return defaultValue; 1444 } 1445 else if (valueString.equalsIgnoreCase("true")) 1446 { 1447 return true; 1448 } 1449 else if (valueString.equalsIgnoreCase("false")) 1450 { 1451 return false; 1452 } 1453 else 1454 { 1455 throw new TaskException(ERR_TASK_CANNOT_PARSE_BOOLEAN.get( 1456 taskEntry.getDN(), valueString, 1457 attributeName)); 1458 } 1459 } 1460 1461 1462 1463 /** 1464 * Parses the values of the specified attribute as a list of strings. 1465 * 1466 * @param taskEntry The entry containing the attribute to be parsed. 1467 * @param attributeName The name of the attribute from which the value was 1468 * taken. 1469 * 1470 * @return A list of strings containing the values of the specified 1471 * attribute, or an empty list if the specified attribute does not 1472 * exist in the target entry. The returned list will be 1473 * unmodifiable. 1474 */ 1475 protected static List<String> parseStringList(final Entry taskEntry, 1476 final String attributeName) 1477 { 1478 final String[] valueStrings = taskEntry.getAttributeValues(attributeName); 1479 if (valueStrings == null) 1480 { 1481 return Collections.emptyList(); 1482 } 1483 else 1484 { 1485 return Collections.unmodifiableList(Arrays.asList(valueStrings)); 1486 } 1487 } 1488 1489 1490 1491 /** 1492 * Parses the provided set of values for the associated task property as a 1493 * {@code Boolean}. 1494 * 1495 * @param p The task property with which the values are 1496 * associated. 1497 * @param values The provided values for the task property. 1498 * @param defaultValue The default value to use if the provided object array 1499 * is empty. 1500 * 1501 * @return The parsed {@code Boolean} value. 1502 * 1503 * @throws TaskException If there is a problem with the provided values. 1504 */ 1505 protected static Boolean parseBoolean(final TaskProperty p, 1506 final List<Object> values, 1507 final Boolean defaultValue) 1508 throws TaskException 1509 { 1510 // Check to see if any values were provided. If not, then it may or may not 1511 // be a problem. 1512 if (values.isEmpty()) 1513 { 1514 if (p.isRequired()) 1515 { 1516 throw new TaskException(ERR_TASK_REQUIRED_PROPERTY_WITHOUT_VALUES.get( 1517 p.getDisplayName())); 1518 } 1519 else 1520 { 1521 return defaultValue; 1522 } 1523 } 1524 1525 // If there were multiple values, then that's always an error. 1526 if (values.size() > 1) 1527 { 1528 throw new TaskException(ERR_TASK_PROPERTY_NOT_MULTIVALUED.get( 1529 p.getDisplayName())); 1530 } 1531 1532 // Make sure that the value can be interpreted as a Boolean. 1533 final Boolean booleanValue; 1534 final Object o = values.get(0); 1535 if (o instanceof Boolean) 1536 { 1537 booleanValue = (Boolean) o; 1538 } 1539 else if (o instanceof String) 1540 { 1541 final String valueStr = (String) o; 1542 if (valueStr.equalsIgnoreCase("true")) 1543 { 1544 booleanValue = Boolean.TRUE; 1545 } 1546 else if (valueStr.equalsIgnoreCase("false")) 1547 { 1548 booleanValue = Boolean.FALSE; 1549 } 1550 else 1551 { 1552 throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_BOOLEAN.get( 1553 p.getDisplayName())); 1554 } 1555 } 1556 else 1557 { 1558 throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_BOOLEAN.get( 1559 p.getDisplayName())); 1560 } 1561 1562 return booleanValue; 1563 } 1564 1565 1566 1567 /** 1568 * Parses the provided set of values for the associated task property as a 1569 * {@code Date}. 1570 * 1571 * @param p The task property with which the values are 1572 * associated. 1573 * @param values The provided values for the task property. 1574 * @param defaultValue The default value to use if the provided object array 1575 * is empty. 1576 * 1577 * @return The parsed {@code Date} value. 1578 * 1579 * @throws TaskException If there is a problem with the provided values. 1580 */ 1581 protected static Date parseDate(final TaskProperty p, 1582 final List<Object> values, 1583 final Date defaultValue) 1584 throws TaskException 1585 { 1586 // Check to see if any values were provided. If not, then it may or may not 1587 // be a problem. 1588 if (values.isEmpty()) 1589 { 1590 if (p.isRequired()) 1591 { 1592 throw new TaskException(ERR_TASK_REQUIRED_PROPERTY_WITHOUT_VALUES.get( 1593 p.getDisplayName())); 1594 } 1595 else 1596 { 1597 return defaultValue; 1598 } 1599 } 1600 1601 // If there were multiple values, then that's always an error. 1602 if (values.size() > 1) 1603 { 1604 throw new TaskException(ERR_TASK_PROPERTY_NOT_MULTIVALUED.get( 1605 p.getDisplayName())); 1606 } 1607 1608 // Make sure that the value can be interpreted as a Date. 1609 final Date dateValue; 1610 final Object o = values.get(0); 1611 if (o instanceof Date) 1612 { 1613 dateValue = (Date) o; 1614 } 1615 else if (o instanceof String) 1616 { 1617 try 1618 { 1619 dateValue = StaticUtils.decodeGeneralizedTime((String) o); 1620 } 1621 catch (final ParseException pe) 1622 { 1623 throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_DATE.get( 1624 p.getDisplayName()), pe); 1625 } 1626 } 1627 else 1628 { 1629 throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_DATE.get( 1630 p.getDisplayName())); 1631 } 1632 1633 // If the task property has a set of allowed values, then make sure that the 1634 // provided value is acceptable. 1635 final Object[] allowedValues = p.getAllowedValues(); 1636 if (allowedValues != null) 1637 { 1638 boolean found = false; 1639 for (final Object allowedValue : allowedValues) 1640 { 1641 if (dateValue.equals(allowedValue)) 1642 { 1643 found = true; 1644 break; 1645 } 1646 } 1647 1648 if (! found) 1649 { 1650 throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_ALLOWED.get( 1651 p.getDisplayName(), dateValue.toString())); 1652 } 1653 } 1654 1655 return dateValue; 1656 } 1657 1658 1659 1660 /** 1661 * Parses the provided set of values for the associated task property as a 1662 * {@code Long}. 1663 * 1664 * @param p The task property with which the values are 1665 * associated. 1666 * @param values The provided values for the task property. 1667 * @param defaultValue The default value to use if the provided object array 1668 * is empty. 1669 * 1670 * @return The parsed {@code Long} value. 1671 * 1672 * @throws TaskException If there is a problem with the provided values. 1673 */ 1674 protected static Long parseLong(final TaskProperty p, 1675 final List<Object> values, 1676 final Long defaultValue) 1677 throws TaskException 1678 { 1679 // Check to see if any values were provided. If not, then it may or may not 1680 // be a problem. 1681 if (values.isEmpty()) 1682 { 1683 if (p.isRequired()) 1684 { 1685 throw new TaskException(ERR_TASK_REQUIRED_PROPERTY_WITHOUT_VALUES.get( 1686 p.getDisplayName())); 1687 } 1688 else 1689 { 1690 return defaultValue; 1691 } 1692 } 1693 1694 // If there were multiple values, then that's always an error. 1695 if (values.size() > 1) 1696 { 1697 throw new TaskException(ERR_TASK_PROPERTY_NOT_MULTIVALUED.get( 1698 p.getDisplayName())); 1699 } 1700 1701 // Make sure that the value can be interpreted as a Long. 1702 final Long longValue; 1703 final Object o = values.get(0); 1704 if (o instanceof Long) 1705 { 1706 longValue = (Long) o; 1707 } 1708 else if (o instanceof Number) 1709 { 1710 longValue = ((Number) o).longValue(); 1711 } 1712 else if (o instanceof String) 1713 { 1714 try 1715 { 1716 longValue = Long.parseLong((String) o); 1717 } 1718 catch (final Exception e) 1719 { 1720 throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_LONG.get( 1721 p.getDisplayName()), e); 1722 } 1723 } 1724 else 1725 { 1726 throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_LONG.get( 1727 p.getDisplayName())); 1728 } 1729 1730 // If the task property has a set of allowed values, then make sure that the 1731 // provided value is acceptable. 1732 final Object[] allowedValues = p.getAllowedValues(); 1733 if (allowedValues != null) 1734 { 1735 boolean found = false; 1736 for (final Object allowedValue : allowedValues) 1737 { 1738 if (longValue.equals(allowedValue)) 1739 { 1740 found = true; 1741 break; 1742 } 1743 } 1744 1745 if (! found) 1746 { 1747 throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_ALLOWED.get( 1748 p.getDisplayName(), longValue.toString())); 1749 } 1750 } 1751 1752 return longValue; 1753 } 1754 1755 1756 1757 /** 1758 * Parses the provided set of values for the associated task property as a 1759 * {@code String}. 1760 * 1761 * @param p The task property with which the values are 1762 * associated. 1763 * @param values The provided values for the task property. 1764 * @param defaultValue The default value to use if the provided object array 1765 * is empty. 1766 * 1767 * @return The parsed {@code String} value. 1768 * 1769 * @throws TaskException If there is a problem with the provided values. 1770 */ 1771 protected static String parseString(final TaskProperty p, 1772 final List<Object> values, 1773 final String defaultValue) 1774 throws TaskException 1775 { 1776 // Check to see if any values were provided. If not, then it may or may not 1777 // be a problem. 1778 if (values.isEmpty()) 1779 { 1780 if (p.isRequired()) 1781 { 1782 throw new TaskException(ERR_TASK_REQUIRED_PROPERTY_WITHOUT_VALUES.get( 1783 p.getDisplayName())); 1784 } 1785 else 1786 { 1787 return defaultValue; 1788 } 1789 } 1790 1791 // If there were multiple values, then that's always an error. 1792 if (values.size() > 1) 1793 { 1794 throw new TaskException(ERR_TASK_PROPERTY_NOT_MULTIVALUED.get( 1795 p.getDisplayName())); 1796 } 1797 1798 // Make sure that the value is a String. 1799 final String valueStr; 1800 final Object o = values.get(0); 1801 if (o instanceof String) 1802 { 1803 valueStr = (String) o; 1804 } 1805 else if (values.get(0) instanceof CharSequence) 1806 { 1807 valueStr = o.toString(); 1808 } 1809 else 1810 { 1811 throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_STRING.get( 1812 p.getDisplayName())); 1813 } 1814 1815 // If the task property has a set of allowed values, then make sure that the 1816 // provided value is acceptable. 1817 final Object[] allowedValues = p.getAllowedValues(); 1818 if (allowedValues != null) 1819 { 1820 boolean found = false; 1821 for (final Object allowedValue : allowedValues) 1822 { 1823 final String s = (String) allowedValue; 1824 if (valueStr.equalsIgnoreCase(s)) 1825 { 1826 found = true; 1827 break; 1828 } 1829 } 1830 1831 if (! found) 1832 { 1833 throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_ALLOWED.get( 1834 p.getDisplayName(), valueStr)); 1835 } 1836 } 1837 1838 return valueStr; 1839 } 1840 1841 1842 1843 /** 1844 * Parses the provided set of values for the associated task property as a 1845 * {@code String} array. 1846 * 1847 * @param p The task property with which the values are 1848 * associated. 1849 * @param values The provided values for the task property. 1850 * @param defaultValues The set of default values to use if the provided 1851 * object array is empty. 1852 * 1853 * @return The parsed {@code String} values. 1854 * 1855 * @throws TaskException If there is a problem with the provided values. 1856 */ 1857 protected static String[] parseStrings(final TaskProperty p, 1858 final List<Object> values, 1859 final String[] defaultValues) 1860 throws TaskException 1861 { 1862 // Check to see if any values were provided. If not, then it may or may not 1863 // be a problem. 1864 if (values.isEmpty()) 1865 { 1866 if (p.isRequired()) 1867 { 1868 throw new TaskException(ERR_TASK_REQUIRED_PROPERTY_WITHOUT_VALUES.get( 1869 p.getDisplayName())); 1870 } 1871 else 1872 { 1873 return defaultValues; 1874 } 1875 } 1876 1877 1878 // Iterate through each of the values and perform appropriate validation for 1879 // them. 1880 final String[] stringValues = new String[values.size()]; 1881 for (int i=0; i < values.size(); i++) 1882 { 1883 final Object o = values.get(i); 1884 1885 // Make sure that the value is a String. 1886 final String valueStr; 1887 if (o instanceof String) 1888 { 1889 valueStr = (String) o; 1890 } 1891 else if (o instanceof CharSequence) 1892 { 1893 valueStr = o.toString(); 1894 } 1895 else 1896 { 1897 throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_STRING.get( 1898 p.getDisplayName())); 1899 } 1900 1901 // If the task property has a set of allowed values, then make sure that 1902 // the provided value is acceptable. 1903 final Object[] allowedValues = p.getAllowedValues(); 1904 if (allowedValues != null) 1905 { 1906 boolean found = false; 1907 for (final Object allowedValue : allowedValues) 1908 { 1909 final String s = (String) allowedValue; 1910 if (valueStr.equalsIgnoreCase(s)) 1911 { 1912 found = true; 1913 break; 1914 } 1915 } 1916 1917 if (! found) 1918 { 1919 throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_ALLOWED.get( 1920 p.getDisplayName(), valueStr)); 1921 } 1922 } 1923 1924 stringValues[i] = valueStr; 1925 } 1926 1927 return stringValues; 1928 } 1929 1930 1931 1932 /** 1933 * Retrieves a list of the additional object classes (other than the base 1934 * "top" and "ds-task" classes) that should be included when creating new task 1935 * entries of this type. 1936 * 1937 * @return A list of the additional object classes that should be included in 1938 * new task entries of this type, or an empty list if there do not 1939 * need to be any additional classes. 1940 */ 1941 protected List<String> getAdditionalObjectClasses() 1942 { 1943 return Collections.emptyList(); 1944 } 1945 1946 1947 1948 /** 1949 * Retrieves a list of the additional attributes (other than attributes common 1950 * to all task types) that should be included when creating new task entries 1951 * of this type. 1952 * 1953 * @return A list of the additional attributes that should be included in new 1954 * task entries of this type, or an empty list if there do not need 1955 * to be any additional attributes. 1956 */ 1957 protected List<Attribute> getAdditionalAttributes() 1958 { 1959 return Collections.emptyList(); 1960 } 1961 1962 1963 1964 /** 1965 * Decodes the provided entry as a scheduled task. An attempt will be made to 1966 * decode the entry as an appropriate subclass if possible, but it will fall 1967 * back to a generic task if it is not possible to decode as a more specific 1968 * task type. 1969 * 1970 * @param entry The entry to be decoded. 1971 * 1972 * @return The decoded task. 1973 * 1974 * @throws TaskException If the provided entry cannot be parsed as a 1975 * scheduled task. 1976 */ 1977 public static Task decodeTask(final Entry entry) 1978 throws TaskException 1979 { 1980 final String taskClass = entry.getAttributeValue(ATTR_TASK_CLASS); 1981 if (taskClass == null) 1982 { 1983 throw new TaskException(ERR_TASK_NO_CLASS.get(entry.getDN())); 1984 } 1985 1986 try 1987 { 1988 if (taskClass.equals(AddSchemaFileTask.ADD_SCHEMA_FILE_TASK_CLASS)) 1989 { 1990 return new AddSchemaFileTask(entry); 1991 } 1992 else if (taskClass.equals(AlertTask.ALERT_TASK_CLASS)) 1993 { 1994 return new AlertTask(entry); 1995 } 1996 else if (taskClass.equals(AuditDataSecurityTask. 1997 AUDIT_DATA_SECURITY_TASK_CLASS)) 1998 { 1999 return new AuditDataSecurityTask(entry); 2000 } 2001 else if (taskClass.equals(BackupTask.BACKUP_TASK_CLASS)) 2002 { 2003 return new BackupTask(entry); 2004 } 2005 else if (taskClass.equals( 2006 CollectSupportDataTask.COLLECT_SUPPORT_DATA_TASK_CLASS)) 2007 { 2008 return new CollectSupportDataTask(entry); 2009 } 2010 else if (taskClass.equals(DelayTask.DELAY_TASK_CLASS)) 2011 { 2012 return new DelayTask(entry); 2013 } 2014 else if (taskClass.equals( 2015 DisconnectClientTask.DISCONNECT_CLIENT_TASK_CLASS)) 2016 { 2017 return new DisconnectClientTask(entry); 2018 } 2019 else if (taskClass.equals(DumpDBDetailsTask.DUMP_DB_DETAILS_TASK_CLASS)) 2020 { 2021 return new DumpDBDetailsTask(entry); 2022 } 2023 else if (taskClass.equals( 2024 EnterLockdownModeTask.ENTER_LOCKDOWN_MODE_TASK_CLASS)) 2025 { 2026 return new EnterLockdownModeTask(entry); 2027 } 2028 else if (taskClass.equals(ExecTask.EXEC_TASK_CLASS)) 2029 { 2030 return new ExecTask(entry); 2031 } 2032 else if (taskClass.equals(ExportTask.EXPORT_TASK_CLASS)) 2033 { 2034 return new ExportTask(entry); 2035 } 2036 else if (taskClass.equals(FileRetentionTask.FILE_RETENTION_TASK_CLASS)) 2037 { 2038 return new FileRetentionTask(entry); 2039 } 2040 else if (taskClass.equals(GroovyScriptedTask.GROOVY_SCRIPTED_TASK_CLASS)) 2041 { 2042 return new GroovyScriptedTask(entry); 2043 } 2044 else if (taskClass.equals(ImportTask.IMPORT_TASK_CLASS)) 2045 { 2046 return new ImportTask(entry); 2047 } 2048 else if (taskClass.equals( 2049 LeaveLockdownModeTask.LEAVE_LOCKDOWN_MODE_TASK_CLASS)) 2050 { 2051 return new LeaveLockdownModeTask(entry); 2052 } 2053 else if (taskClass.equals(RebuildTask.REBUILD_TASK_CLASS)) 2054 { 2055 return new RebuildTask(entry); 2056 } 2057 else if (taskClass.equals( 2058 ReEncodeEntriesTask.RE_ENCODE_ENTRIES_TASK_CLASS)) 2059 { 2060 return new ReEncodeEntriesTask(entry); 2061 } 2062 else if (taskClass.equals(RefreshEncryptionSettingsTask. 2063 REFRESH_ENCRYPTION_SETTINGS_TASK_CLASS)) 2064 { 2065 return new RefreshEncryptionSettingsTask(entry); 2066 } 2067 else if (taskClass.equals( 2068 ReloadGlobalIndexTask.RELOAD_GLOBAL_INDEX_TASK_CLASS)) 2069 { 2070 return new ReloadGlobalIndexTask(entry); 2071 } 2072 else if (taskClass.equals( 2073 ReloadHTTPConnectionHandlerCertificatesTask. 2074 RELOAD_HTTP_CONNECTION_HANDLER_CERTIFICATES_TASK_CLASS)) 2075 { 2076 return new ReloadHTTPConnectionHandlerCertificatesTask(entry); 2077 } 2078 else if (taskClass.equals(RestoreTask.RESTORE_TASK_CLASS)) 2079 { 2080 return new RestoreTask(entry); 2081 } 2082 else if (taskClass.equals(RotateLogTask.ROTATE_LOG_TASK_CLASS)) 2083 { 2084 return new RotateLogTask(entry); 2085 } 2086 else if (taskClass.equals(SearchTask.SEARCH_TASK_CLASS)) 2087 { 2088 return new SearchTask(entry); 2089 } 2090 else if (taskClass.equals(ShutdownTask.SHUTDOWN_TASK_CLASS)) 2091 { 2092 return new ShutdownTask(entry); 2093 } 2094 else if (taskClass.equals(SynchronizeEncryptionSettingsTask. 2095 SYNCHRONIZE_ENCRYPTION_SETTINGS_TASK_CLASS)) 2096 { 2097 return new SynchronizeEncryptionSettingsTask(entry); 2098 } 2099 else if (taskClass.equals(ThirdPartyTask.THIRD_PARTY_TASK_CLASS)) 2100 { 2101 return new ThirdPartyTask(entry); 2102 } 2103 } 2104 catch (final TaskException te) 2105 { 2106 Debug.debugException(te); 2107 } 2108 2109 return new Task(entry); 2110 } 2111 2112 2113 2114 /** 2115 * Retrieves a list of task properties that may be provided when scheduling 2116 * any type of task. This includes: 2117 * <UL> 2118 * <LI>The task ID</LI> 2119 * <LI>The scheduled start time</LI> 2120 * <LI>The task IDs of any tasks on which this task is dependent</LI> 2121 * <LI>The action to take for this task if any of its dependencies fail</LI> 2122 * <LI>The addresses of users to notify when this task starts</LI> 2123 * <LI>The addresses of users to notify when this task completes</LI> 2124 * <LI>The addresses of users to notify if this task succeeds</LI> 2125 * <LI>The addresses of users to notify if this task fails</LI> 2126 * <LI>A flag indicating whether to generate an alert when the task 2127 * starts</LI> 2128 * <LI>A flag indicating whether to generate an alert when the task 2129 * succeeds</LI> 2130 * <LI>A flag indicating whether to generate an alert when the task 2131 * fails</LI> 2132 * </UL> 2133 * 2134 * @return A list of task properties that may be provided when scheduling any 2135 * type of task. 2136 */ 2137 public static List<TaskProperty> getCommonTaskProperties() 2138 { 2139 final List<TaskProperty> taskList = Arrays.asList( 2140 PROPERTY_TASK_ID, 2141 PROPERTY_SCHEDULED_START_TIME, 2142 PROPERTY_DEPENDENCY_ID, 2143 PROPERTY_FAILED_DEPENDENCY_ACTION, 2144 PROPERTY_NOTIFY_ON_START, 2145 PROPERTY_NOTIFY_ON_COMPLETION, 2146 PROPERTY_NOTIFY_ON_SUCCESS, 2147 PROPERTY_NOTIFY_ON_ERROR, 2148 PROPERTY_ALERT_ON_START, 2149 PROPERTY_ALERT_ON_SUCCESS, 2150 PROPERTY_ALERT_ON_ERROR); 2151 2152 return Collections.unmodifiableList(taskList); 2153 } 2154 2155 2156 2157 /** 2158 * Retrieves a list of task-specific properties that may be provided when 2159 * scheduling a task of this type. This method should be overridden by 2160 * subclasses in order to provide an appropriate set of properties. 2161 * 2162 * @return A list of task-specific properties that may be provided when 2163 * scheduling a task of this type. 2164 */ 2165 public List<TaskProperty> getTaskSpecificProperties() 2166 { 2167 return Collections.emptyList(); 2168 } 2169 2170 2171 2172 /** 2173 * Retrieves the values of the task properties for this task. The data type 2174 * of the values will vary based on the data type of the corresponding task 2175 * property and may be one of the following types: {@code Boolean}, 2176 * {@code Date}, {@code Long}, or {@code String}. Task properties which do 2177 * not have any values will be included in the map with an empty value list. 2178 * <BR><BR> 2179 * Note that subclasses which have additional task properties should override 2180 * this method and return a map which contains both the property values from 2181 * this class (obtained from {@code super.getTaskPropertyValues()} and the 2182 * values of their own task-specific properties. 2183 * 2184 * @return A map of the task property values for this task. 2185 */ 2186 public Map<TaskProperty,List<Object>> getTaskPropertyValues() 2187 { 2188 final LinkedHashMap<TaskProperty,List<Object>> props = 2189 new LinkedHashMap<>(StaticUtils.computeMapCapacity(20)); 2190 2191 props.put(PROPERTY_TASK_ID, 2192 Collections.<Object>singletonList(taskID)); 2193 2194 if (scheduledStartTime == null) 2195 { 2196 props.put(PROPERTY_SCHEDULED_START_TIME, Collections.emptyList()); 2197 } 2198 else 2199 { 2200 props.put(PROPERTY_SCHEDULED_START_TIME, 2201 Collections.<Object>singletonList(scheduledStartTime)); 2202 } 2203 2204 props.put(PROPERTY_DEPENDENCY_ID, 2205 Collections.<Object>unmodifiableList(dependencyIDs)); 2206 2207 if (failedDependencyAction == null) 2208 { 2209 props.put(PROPERTY_FAILED_DEPENDENCY_ACTION, Collections.emptyList()); 2210 } 2211 else 2212 { 2213 props.put(PROPERTY_FAILED_DEPENDENCY_ACTION, 2214 Collections.<Object>singletonList(failedDependencyAction.getName())); 2215 } 2216 2217 props.put(PROPERTY_NOTIFY_ON_START, 2218 Collections.<Object>unmodifiableList(notifyOnStart)); 2219 2220 props.put(PROPERTY_NOTIFY_ON_COMPLETION, 2221 Collections.<Object>unmodifiableList(notifyOnCompletion)); 2222 2223 props.put(PROPERTY_NOTIFY_ON_SUCCESS, 2224 Collections.<Object>unmodifiableList(notifyOnSuccess)); 2225 2226 props.put(PROPERTY_NOTIFY_ON_ERROR, 2227 Collections.<Object>unmodifiableList(notifyOnError)); 2228 2229 if (alertOnStart != null) 2230 { 2231 props.put(PROPERTY_ALERT_ON_START, 2232 Collections.<Object>singletonList(alertOnStart)); 2233 } 2234 2235 if (alertOnSuccess != null) 2236 { 2237 props.put(PROPERTY_ALERT_ON_SUCCESS, 2238 Collections.<Object>singletonList(alertOnSuccess)); 2239 } 2240 2241 if (alertOnError!= null) 2242 { 2243 props.put(PROPERTY_ALERT_ON_ERROR, 2244 Collections.<Object>singletonList(alertOnError)); 2245 } 2246 2247 return Collections.unmodifiableMap(props); 2248 } 2249 2250 2251 2252 /** 2253 * Retrieves a string representation of this task. 2254 * 2255 * @return A string representation of this task. 2256 */ 2257 @Override() 2258 public final String toString() 2259 { 2260 final StringBuilder buffer = new StringBuilder(); 2261 toString(buffer); 2262 return buffer.toString(); 2263 } 2264 2265 2266 2267 /** 2268 * Appends a string representation of this task to the provided buffer. 2269 * 2270 * @param buffer The buffer to which the string representation should be 2271 * provided. 2272 */ 2273 public final void toString(final StringBuilder buffer) 2274 { 2275 buffer.append("Task(name='"); 2276 buffer.append(getTaskName()); 2277 buffer.append("', className='"); 2278 buffer.append(taskClassName); 2279 buffer.append(", properties={"); 2280 2281 boolean added = false; 2282 for (final Map.Entry<TaskProperty,List<Object>> e : 2283 getTaskPropertyValues().entrySet()) 2284 { 2285 if (added) 2286 { 2287 buffer.append(", "); 2288 } 2289 else 2290 { 2291 added = true; 2292 } 2293 2294 buffer.append(e.getKey().getAttributeName()); 2295 buffer.append("={"); 2296 2297 final Iterator<Object> iterator = e.getValue().iterator(); 2298 while (iterator.hasNext()) 2299 { 2300 buffer.append('\''); 2301 buffer.append(String.valueOf(iterator.next())); 2302 buffer.append('\''); 2303 2304 if (iterator.hasNext()) 2305 { 2306 buffer.append(','); 2307 } 2308 } 2309 2310 buffer.append('}'); 2311 } 2312 2313 buffer.append("})"); 2314 } 2315}