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