001/*
002 * Copyright 2008-2021 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2008-2021 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-2021 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;
050
051import com.unboundid.ldap.sdk.Attribute;
052import com.unboundid.ldap.sdk.Entry;
053import com.unboundid.util.CryptoHelper;
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 = CryptoHelper.getRandomUUID().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  = CryptoHelper.getRandomUUID().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 RemoveAttributeTypeTask(),
1017         new RemoveObjectClassTask(),
1018         new RestoreTask(),
1019         new RotateLogTask(),
1020         new SearchTask(),
1021         new ShutdownTask(),
1022         new SynchronizeEncryptionSettingsTask(),
1023         new ThirdPartyTask());
1024
1025    return Collections.unmodifiableList(taskList);
1026  }
1027
1028
1029
1030  /**
1031   * Retrieves a human-readable name for this task.
1032   *
1033   * @return  A human-readable name for this task.
1034   */
1035  @NotNull()
1036  public String getTaskName()
1037  {
1038    return INFO_TASK_NAME_GENERIC.get();
1039  }
1040
1041
1042
1043  /**
1044   * Retrieves a human-readable description for this task.
1045   *
1046   * @return  A human-readable description for this task.
1047   */
1048  @NotNull()
1049  public String getTaskDescription()
1050  {
1051    return INFO_TASK_DESCRIPTION_GENERIC.get();
1052  }
1053
1054
1055
1056  /**
1057   * Retrieves the entry from which this task was decoded, if available.  Note
1058   * that although the entry is not immutable, changes made to it will not be
1059   * reflected in this task.
1060   *
1061   * @return  The entry from which this task was decoded, or {@code null} if
1062   *          this task was not created from an existing entry.
1063   */
1064  @Nullable()
1065  protected final Entry getTaskEntry()
1066  {
1067    return taskEntry;
1068  }
1069
1070
1071
1072  /**
1073   * Retrieves the DN of the entry in which this scheduled task is defined.
1074   *
1075   * @return  The DN of the entry in which this scheduled task is defined.
1076   */
1077  @NotNull()
1078  public final String getTaskEntryDN()
1079  {
1080    return taskEntryDN;
1081  }
1082
1083
1084
1085  /**
1086   * Retrieves the task ID for this task.
1087   *
1088   * @return  The task ID for this task.
1089   */
1090  @NotNull()
1091  public final String getTaskID()
1092  {
1093    return taskID;
1094  }
1095
1096
1097
1098  /**
1099   * Retrieves the fully-qualified name of the Java class that provides the
1100   * logic for this class.
1101   *
1102   * @return  The fully-qualified name of the Java class that provides the logic
1103   *          for this task.
1104   */
1105  @NotNull()
1106  public final String getTaskClassName()
1107  {
1108    return taskClassName;
1109  }
1110
1111
1112
1113  /**
1114   * Retrieves the current state for this task.
1115   *
1116   * @return  The current state for this task.
1117   */
1118  @NotNull()
1119  public final TaskState getState()
1120  {
1121    return taskState;
1122  }
1123
1124
1125
1126  /**
1127   * Indicates whether this task is currently pending execution.
1128   *
1129   * @return  {@code true} if this task is currently pending execution, or
1130   *          {@code false} if not.
1131   */
1132  public final boolean isPending()
1133  {
1134    return taskState.isPending();
1135  }
1136
1137
1138
1139  /**
1140   * Indicates whether this task is currently running.
1141   *
1142   * @return  {@code true} if this task is currently running, or {@code false}
1143   *          if not.
1144   */
1145  public final boolean isRunning()
1146  {
1147    return taskState.isRunning();
1148  }
1149
1150
1151
1152  /**
1153   * Indicates whether this task has completed execution.
1154   *
1155   * @return  {@code true} if this task has completed execution, or
1156   *          {@code false} if not.
1157   */
1158  public final boolean isCompleted()
1159  {
1160    return taskState.isCompleted();
1161  }
1162
1163
1164
1165  /**
1166   * Retrieves the time that this task is/was scheduled to start running.
1167   *
1168   * @return  The time that this task is/was scheduled to start running, or
1169   *          {@code null} if that is not available and therefore the task
1170   *          should start running as soon as all dependencies have been met.
1171   */
1172  @Nullable()
1173  public final Date getScheduledStartTime()
1174  {
1175    return scheduledStartTime;
1176  }
1177
1178
1179
1180  /**
1181   * Retrieves the time that this task actually started running.
1182   *
1183   * @return  The time that this task actually started running, or {@code null}
1184   *          if that is not available (e.g., because the task has not yet
1185   *          started).
1186   */
1187  @Nullable()
1188  public final Date getActualStartTime()
1189  {
1190    return actualStartTime;
1191  }
1192
1193
1194
1195  /**
1196   * Retrieves the time that this task completed.
1197   *
1198   * @return  The time that this task completed, or {@code null} if it has not
1199   *          yet completed.
1200   */
1201  @Nullable()
1202  public final Date getCompletionTime()
1203  {
1204    return completionTime;
1205  }
1206
1207
1208
1209  /**
1210   * Retrieves a list of the task IDs for tasks that must complete before this
1211   * task will be eligible to start.
1212   *
1213   * @return  A list of the task IDs for tasks that must complete before this
1214   *          task will be eligible to start, or an empty list if this task does
1215   *          not have any dependencies.
1216   */
1217  @NotNull()
1218  public final List<String> getDependencyIDs()
1219  {
1220    return dependencyIDs;
1221  }
1222
1223
1224
1225  /**
1226   * Retrieves the failed dependency action for this task, which indicates the
1227   * behavior that it should exhibit if any of its dependencies encounter a
1228   * failure.
1229   *
1230   * @return  The failed dependency action for this task, or {@code null} if it
1231   *          is not available.
1232   */
1233  @Nullable()
1234  public final FailedDependencyAction getFailedDependencyAction()
1235  {
1236    return failedDependencyAction;
1237  }
1238
1239
1240
1241  /**
1242   * Retrieves the log messages for this task.  Note that if the task has
1243   * generated a very large number of log messages, then only a portion of the
1244   * most recent messages may be available.
1245   *
1246   * @return  The log messages for this task, or an empty list if this task does
1247   *          not have any log messages.
1248   */
1249  @NotNull()
1250  public final List<String> getLogMessages()
1251  {
1252    return logMessages;
1253  }
1254
1255
1256
1257  /**
1258   * Retrieves a list of the e-mail addresses of the individuals that should be
1259   * notified whenever this task starts running.
1260   *
1261   * @return  A list of the e-mail addresses of the individuals that should be
1262   *          notified whenever this task starts running, or an empty list if
1263   *          there are none.
1264   */
1265  @NotNull()
1266  public final List<String> getNotifyOnStartAddresses()
1267  {
1268    return notifyOnStart;
1269  }
1270
1271
1272
1273  /**
1274   * Retrieves a list of the e-mail addresses of the individuals that should be
1275   * notified whenever this task completes processing, regardless of whether it
1276   * was successful.
1277   *
1278   * @return  A list of the e-mail addresses of the individuals that should be
1279   *          notified whenever this task completes processing, or an empty list
1280   *          if there are none.
1281   */
1282  @NotNull()
1283  public final List<String> getNotifyOnCompletionAddresses()
1284  {
1285    return notifyOnCompletion;
1286  }
1287
1288
1289
1290  /**
1291   * Retrieves a list of the e-mail addresses of the individuals that should be
1292   * notified if this task completes successfully.
1293   *
1294   * @return  A list of the e-mail addresses of the individuals that should be
1295   *          notified if this task completes successfully, or an empty list
1296   *          if there are none.
1297   */
1298  @NotNull()
1299  public final List<String> getNotifyOnSuccessAddresses()
1300  {
1301    return notifyOnSuccess;
1302  }
1303
1304
1305
1306  /**
1307   * Retrieves a list of the e-mail addresses of the individuals that should be
1308   * notified if this task stops processing prematurely due to an error or
1309   * other external action (e.g., server shutdown or administrative cancel).
1310   *
1311   * @return  A list of the e-mail addresses of the individuals that should be
1312   *          notified if this task stops processing prematurely, or an empty
1313   *          list if there are none.
1314   */
1315  @NotNull()
1316  public final List<String> getNotifyOnErrorAddresses()
1317  {
1318    return notifyOnError;
1319  }
1320
1321
1322
1323  /**
1324   * Retrieves the flag that indicates whether the server should generate an
1325   * administrative alert when this task starts running.
1326   *
1327   * @return  {@code true} if the server should send an alert when this task
1328   *          starts running, {@code false} if the server should not send an
1329   *          alert, or {@code null} if it is not available.
1330   */
1331  @Nullable()
1332  public final Boolean getAlertOnStart()
1333  {
1334    return alertOnStart;
1335  }
1336
1337
1338
1339  /**
1340   * Retrieves the flag that indicates whether the server should generate an
1341   * administrative alert if this task completes successfully.
1342   *
1343   * @return  {@code true} if the server should send an alert if this task
1344   *          completes successfully, {@code false} if the server should not
1345   *          send an alert, or {@code null} if it is not available.
1346   */
1347  @Nullable()
1348  public final Boolean getAlertOnSuccess()
1349  {
1350    return alertOnSuccess;
1351  }
1352
1353
1354
1355  /**
1356   * Retrieves the flag that indicates whether the server should generate an
1357   * administrative alert if this task fails to complete successfully.
1358   *
1359   * @return  {@code true} if the server should send an alert if this task fails
1360   *          to complete successfully, {@code false} if the server should not
1361   *          send an alert, or {@code null} if it is not available.
1362   */
1363  @Nullable()
1364  public final Boolean getAlertOnError()
1365  {
1366    return alertOnError;
1367  }
1368
1369
1370
1371  /**
1372   * Creates an entry that may be added to the Directory Server to create a new
1373   * instance of this task.
1374   *
1375   * @return  An entry that may be added to the Directory Server to create a new
1376   *          instance of this task.
1377   */
1378  @NotNull()
1379  public final Entry createTaskEntry()
1380  {
1381    final ArrayList<Attribute> attributes = new ArrayList<>(20);
1382
1383    final ArrayList<String> ocValues = new ArrayList<>(5);
1384    ocValues.add("top");
1385    ocValues.add(OC_TASK);
1386    ocValues.addAll(getAdditionalObjectClasses());
1387    attributes.add(new Attribute("objectClass", ocValues));
1388
1389    attributes.add(new Attribute(ATTR_TASK_ID, taskID));
1390
1391    attributes.add(new Attribute(ATTR_TASK_CLASS, taskClassName));
1392
1393    if (scheduledStartTime != null)
1394    {
1395      attributes.add(new Attribute(ATTR_SCHEDULED_START_TIME,
1396           StaticUtils.encodeGeneralizedTime(scheduledStartTime)));
1397    }
1398
1399    if (! dependencyIDs.isEmpty())
1400    {
1401      attributes.add(new Attribute(ATTR_DEPENDENCY_ID, dependencyIDs));
1402    }
1403
1404    if (failedDependencyAction != null)
1405    {
1406      attributes.add(new Attribute(ATTR_FAILED_DEPENDENCY_ACTION,
1407                                   failedDependencyAction.getName()));
1408    }
1409
1410    if (! notifyOnStart.isEmpty())
1411    {
1412      attributes.add(new Attribute(ATTR_NOTIFY_ON_START,
1413                                   notifyOnStart));
1414    }
1415
1416    if (! notifyOnCompletion.isEmpty())
1417    {
1418      attributes.add(new Attribute(ATTR_NOTIFY_ON_COMPLETION,
1419                                   notifyOnCompletion));
1420    }
1421
1422    if (! notifyOnSuccess.isEmpty())
1423    {
1424      attributes.add(new Attribute(ATTR_NOTIFY_ON_SUCCESS, notifyOnSuccess));
1425    }
1426
1427    if (! notifyOnError.isEmpty())
1428    {
1429      attributes.add(new Attribute(ATTR_NOTIFY_ON_ERROR, notifyOnError));
1430    }
1431
1432    if (alertOnStart != null)
1433    {
1434      attributes.add(new Attribute(ATTR_ALERT_ON_START,
1435           String.valueOf(alertOnStart)));
1436    }
1437
1438    if (alertOnSuccess != null)
1439    {
1440      attributes.add(new Attribute(ATTR_ALERT_ON_SUCCESS,
1441           String.valueOf(alertOnSuccess)));
1442    }
1443
1444    if (alertOnError != null)
1445    {
1446      attributes.add(new Attribute(ATTR_ALERT_ON_ERROR,
1447           String.valueOf(alertOnError)));
1448    }
1449
1450    attributes.addAll(getAdditionalAttributes());
1451
1452    return new Entry(taskEntryDN, attributes);
1453  }
1454
1455
1456
1457  /**
1458   * Parses the value of the specified attribute as a {@code boolean} value, or
1459   * throws an exception if the value cannot be decoded as a boolean.
1460   *
1461   * @param  taskEntry      The entry containing the attribute to be parsed.
1462   * @param  attributeName  The name of the attribute from which the value was
1463   *                        taken.
1464   * @param  defaultValue   The default value to use if the provided value
1465   *                        string is {@code null}.
1466   *
1467   * @return  {@code true} if the value string represents a boolean value of
1468   *          {@code true}, {@code false} if the value string represents a
1469   *          boolean value of {@code false}, or the default value if the value
1470   *          string is {@code null}.
1471   *
1472   * @throws  TaskException  If the provided value string cannot be parsed as a
1473   *                         {@code boolean} value.
1474   */
1475  protected static boolean parseBooleanValue(@NotNull final Entry taskEntry,
1476                 @NotNull final String attributeName,
1477                 final boolean defaultValue)
1478            throws TaskException
1479  {
1480    final String valueString = taskEntry.getAttributeValue(attributeName);
1481    if (valueString == null)
1482    {
1483      return defaultValue;
1484    }
1485    else if (valueString.equalsIgnoreCase("true"))
1486    {
1487      return true;
1488    }
1489    else if (valueString.equalsIgnoreCase("false"))
1490    {
1491      return false;
1492    }
1493    else
1494    {
1495      throw new TaskException(ERR_TASK_CANNOT_PARSE_BOOLEAN.get(
1496                                   taskEntry.getDN(), valueString,
1497                                   attributeName));
1498    }
1499  }
1500
1501
1502
1503  /**
1504   * Parses the values of the specified attribute as a list of strings.
1505   *
1506   * @param  taskEntry      The entry containing the attribute to be parsed.
1507   * @param  attributeName  The name of the attribute from which the value was
1508   *                        taken.
1509   *
1510   * @return  A list of strings containing the values of the specified
1511   *          attribute, or an empty list if the specified attribute does not
1512   *          exist in the target entry.  The returned list will be
1513   *          unmodifiable.
1514   */
1515  @NotNull()
1516  protected static List<String> parseStringList(@NotNull final Entry taskEntry,
1517                 @NotNull final String attributeName)
1518  {
1519    final String[] valueStrings = taskEntry.getAttributeValues(attributeName);
1520    if (valueStrings == null)
1521    {
1522      return Collections.emptyList();
1523    }
1524    else
1525    {
1526      return Collections.unmodifiableList(Arrays.asList(valueStrings));
1527    }
1528  }
1529
1530
1531
1532  /**
1533   * Parses the provided set of values for the associated task property as a
1534   * {@code Boolean}.
1535   *
1536   * @param  p             The task property with which the values are
1537   *                       associated.
1538   * @param  values        The provided values for the task property.
1539   * @param  defaultValue  The default value to use if the provided object array
1540   *                       is empty.
1541   *
1542   * @return  The parsed {@code Boolean} value.
1543   *
1544   * @throws  TaskException  If there is a problem with the provided values.
1545   */
1546  @Nullable()
1547  protected static Boolean parseBoolean(@NotNull final TaskProperty p,
1548                                        @NotNull final List<Object> values,
1549                                        @Nullable final Boolean defaultValue)
1550            throws TaskException
1551  {
1552    // Check to see if any values were provided.  If not, then it may or may not
1553    // be a problem.
1554    if (values.isEmpty())
1555    {
1556      if (p.isRequired())
1557      {
1558        throw new TaskException(ERR_TASK_REQUIRED_PROPERTY_WITHOUT_VALUES.get(
1559                                     p.getDisplayName()));
1560      }
1561      else
1562      {
1563        return defaultValue;
1564      }
1565    }
1566
1567    // If there were multiple values, then that's always an error.
1568    if (values.size() > 1)
1569    {
1570      throw new TaskException(ERR_TASK_PROPERTY_NOT_MULTIVALUED.get(
1571                                   p.getDisplayName()));
1572    }
1573
1574    // Make sure that the value can be interpreted as a Boolean.
1575    final Boolean booleanValue;
1576    final Object o = values.get(0);
1577    if (o instanceof Boolean)
1578    {
1579      booleanValue = (Boolean) o;
1580    }
1581    else if (o instanceof String)
1582    {
1583      final String valueStr = (String) o;
1584      if (valueStr.equalsIgnoreCase("true"))
1585      {
1586        booleanValue = Boolean.TRUE;
1587      }
1588      else if (valueStr.equalsIgnoreCase("false"))
1589      {
1590        booleanValue = Boolean.FALSE;
1591      }
1592      else
1593      {
1594        throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_BOOLEAN.get(
1595                                     p.getDisplayName()));
1596      }
1597    }
1598    else
1599    {
1600      throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_BOOLEAN.get(
1601                                   p.getDisplayName()));
1602    }
1603
1604    return booleanValue;
1605  }
1606
1607
1608
1609  /**
1610   * Parses the provided set of values for the associated task property as a
1611   * {@code Date}.
1612   *
1613   * @param  p             The task property with which the values are
1614   *                       associated.
1615   * @param  values        The provided values for the task property.
1616   * @param  defaultValue  The default value to use if the provided object array
1617   *                       is empty.
1618   *
1619   * @return  The parsed {@code Date} value.
1620   *
1621   * @throws  TaskException  If there is a problem with the provided values.
1622   */
1623  @Nullable()
1624  protected static Date parseDate(@NotNull final TaskProperty p,
1625                                  @NotNull final List<Object> values,
1626                                  @Nullable final Date defaultValue)
1627            throws TaskException
1628  {
1629    // Check to see if any values were provided.  If not, then it may or may not
1630    // be a problem.
1631    if (values.isEmpty())
1632    {
1633      if (p.isRequired())
1634      {
1635        throw new TaskException(ERR_TASK_REQUIRED_PROPERTY_WITHOUT_VALUES.get(
1636                                     p.getDisplayName()));
1637      }
1638      else
1639      {
1640        return defaultValue;
1641      }
1642    }
1643
1644    // If there were multiple values, then that's always an error.
1645    if (values.size() > 1)
1646    {
1647      throw new TaskException(ERR_TASK_PROPERTY_NOT_MULTIVALUED.get(
1648                                   p.getDisplayName()));
1649    }
1650
1651    // Make sure that the value can be interpreted as a Date.
1652    final Date dateValue;
1653    final Object o = values.get(0);
1654    if (o instanceof Date)
1655    {
1656      dateValue = (Date) o;
1657    }
1658    else if (o instanceof String)
1659    {
1660      try
1661      {
1662        dateValue = StaticUtils.decodeGeneralizedTime((String) o);
1663      }
1664      catch (final ParseException pe)
1665      {
1666        throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_DATE.get(
1667                                     p.getDisplayName()), pe);
1668      }
1669    }
1670    else
1671    {
1672      throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_DATE.get(
1673                                   p.getDisplayName()));
1674    }
1675
1676    // If the task property has a set of allowed values, then make sure that the
1677    // provided value is acceptable.
1678    final Object[] allowedValues = p.getAllowedValues();
1679    if (allowedValues != null)
1680    {
1681      boolean found = false;
1682      for (final Object allowedValue : allowedValues)
1683      {
1684        if (dateValue.equals(allowedValue))
1685        {
1686          found = true;
1687          break;
1688        }
1689      }
1690
1691      if (! found)
1692      {
1693        throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_ALLOWED.get(
1694                                     p.getDisplayName(), dateValue.toString()));
1695      }
1696    }
1697
1698    return dateValue;
1699  }
1700
1701
1702
1703  /**
1704   * Parses the provided set of values for the associated task property as a
1705   * {@code Long}.
1706   *
1707   * @param  p             The task property with which the values are
1708   *                       associated.
1709   * @param  values        The provided values for the task property.
1710   * @param  defaultValue  The default value to use if the provided object array
1711   *                       is empty.
1712   *
1713   * @return  The parsed {@code Long} value.
1714   *
1715   * @throws  TaskException  If there is a problem with the provided values.
1716   */
1717  @Nullable()
1718  protected static Long parseLong(@NotNull final TaskProperty p,
1719                                  @NotNull final List<Object> values,
1720                                  @Nullable final Long defaultValue)
1721            throws TaskException
1722  {
1723    // Check to see if any values were provided.  If not, then it may or may not
1724    // be a problem.
1725    if (values.isEmpty())
1726    {
1727      if (p.isRequired())
1728      {
1729        throw new TaskException(ERR_TASK_REQUIRED_PROPERTY_WITHOUT_VALUES.get(
1730                                     p.getDisplayName()));
1731      }
1732      else
1733      {
1734        return defaultValue;
1735      }
1736    }
1737
1738    // If there were multiple values, then that's always an error.
1739    if (values.size() > 1)
1740    {
1741      throw new TaskException(ERR_TASK_PROPERTY_NOT_MULTIVALUED.get(
1742                                   p.getDisplayName()));
1743    }
1744
1745    // Make sure that the value can be interpreted as a Long.
1746    final Long longValue;
1747    final Object o = values.get(0);
1748    if (o instanceof Long)
1749    {
1750      longValue = (Long) o;
1751    }
1752    else if (o instanceof Number)
1753    {
1754      longValue = ((Number) o).longValue();
1755    }
1756    else if (o instanceof String)
1757    {
1758      try
1759      {
1760        longValue = Long.parseLong((String) o);
1761      }
1762      catch (final Exception e)
1763      {
1764        throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_LONG.get(
1765                                     p.getDisplayName()), e);
1766      }
1767    }
1768    else
1769    {
1770      throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_LONG.get(
1771                                   p.getDisplayName()));
1772    }
1773
1774    // If the task property has a set of allowed values, then make sure that the
1775    // provided value is acceptable.
1776    final Object[] allowedValues = p.getAllowedValues();
1777    if (allowedValues != null)
1778    {
1779      boolean found = false;
1780      for (final Object allowedValue : allowedValues)
1781      {
1782        if (longValue.equals(allowedValue))
1783        {
1784          found = true;
1785          break;
1786        }
1787      }
1788
1789      if (! found)
1790      {
1791        throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_ALLOWED.get(
1792                                     p.getDisplayName(), longValue.toString()));
1793      }
1794    }
1795
1796    return longValue;
1797  }
1798
1799
1800
1801  /**
1802   * Parses the provided set of values for the associated task property as a
1803   * {@code String}.
1804   *
1805   * @param  p             The task property with which the values are
1806   *                       associated.
1807   * @param  values        The provided values for the task property.
1808   * @param  defaultValue  The default value to use if the provided object array
1809   *                       is empty.
1810   *
1811   * @return  The parsed {@code String} value.
1812   *
1813   * @throws  TaskException  If there is a problem with the provided values.
1814   */
1815  @Nullable()
1816  protected static String parseString(@NotNull final TaskProperty p,
1817                                      @NotNull final List<Object> values,
1818                                      @Nullable final String defaultValue)
1819            throws TaskException
1820  {
1821    // Check to see if any values were provided.  If not, then it may or may not
1822    // be a problem.
1823    if (values.isEmpty())
1824    {
1825      if (p.isRequired())
1826      {
1827        throw new TaskException(ERR_TASK_REQUIRED_PROPERTY_WITHOUT_VALUES.get(
1828                                     p.getDisplayName()));
1829      }
1830      else
1831      {
1832        return defaultValue;
1833      }
1834    }
1835
1836    // If there were multiple values, then that's always an error.
1837    if (values.size() > 1)
1838    {
1839      throw new TaskException(ERR_TASK_PROPERTY_NOT_MULTIVALUED.get(
1840                                   p.getDisplayName()));
1841    }
1842
1843    // Make sure that the value is a String.
1844    final String valueStr;
1845    final Object o = values.get(0);
1846    if (o instanceof String)
1847    {
1848      valueStr = (String) o;
1849    }
1850    else if (values.get(0) instanceof CharSequence)
1851    {
1852      valueStr = o.toString();
1853    }
1854    else
1855    {
1856      throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_STRING.get(
1857                                   p.getDisplayName()));
1858    }
1859
1860    // If the task property has a set of allowed values, then make sure that the
1861    // provided value is acceptable.
1862    final Object[] allowedValues = p.getAllowedValues();
1863    if (allowedValues != null)
1864    {
1865      boolean found = false;
1866      for (final Object allowedValue : allowedValues)
1867      {
1868        final String s = (String) allowedValue;
1869        if (valueStr.equalsIgnoreCase(s))
1870        {
1871          found = true;
1872          break;
1873        }
1874      }
1875
1876      if (! found)
1877      {
1878        throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_ALLOWED.get(
1879                                     p.getDisplayName(), valueStr));
1880      }
1881    }
1882
1883    return valueStr;
1884  }
1885
1886
1887
1888  /**
1889   * Parses the provided set of values for the associated task property as a
1890   * {@code String} array.
1891   *
1892   * @param  p              The task property with which the values are
1893   *                        associated.
1894   * @param  values         The provided values for the task property.
1895   * @param  defaultValues  The set of default values to use if the provided
1896   *                        object array is empty.
1897   *
1898   * @return  The parsed {@code String} values.
1899   *
1900   * @throws  TaskException  If there is a problem with the provided values.
1901   */
1902  @Nullable()
1903  protected static String[] parseStrings(@NotNull final TaskProperty p,
1904                                         @NotNull final List<Object> values,
1905                                         @Nullable final String[] defaultValues)
1906            throws TaskException
1907  {
1908    // Check to see if any values were provided.  If not, then it may or may not
1909    // be a problem.
1910    if (values.isEmpty())
1911    {
1912      if (p.isRequired())
1913      {
1914        throw new TaskException(ERR_TASK_REQUIRED_PROPERTY_WITHOUT_VALUES.get(
1915                                     p.getDisplayName()));
1916      }
1917      else
1918      {
1919        return defaultValues;
1920      }
1921    }
1922
1923
1924    // Iterate through each of the values and perform appropriate validation for
1925    // them.
1926    final String[] stringValues = new String[values.size()];
1927    for (int i=0; i < values.size(); i++)
1928    {
1929      final Object o = values.get(i);
1930
1931      // Make sure that the value is a String.
1932      final String valueStr;
1933      if (o instanceof String)
1934      {
1935        valueStr = (String) o;
1936      }
1937      else if (o instanceof CharSequence)
1938      {
1939        valueStr = o.toString();
1940      }
1941      else
1942      {
1943        throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_STRING.get(
1944                                     p.getDisplayName()));
1945      }
1946
1947      // If the task property has a set of allowed values, then make sure that
1948      // the provided value is acceptable.
1949      final Object[] allowedValues = p.getAllowedValues();
1950      if (allowedValues != null)
1951      {
1952        boolean found = false;
1953        for (final Object allowedValue : allowedValues)
1954        {
1955          final String s = (String) allowedValue;
1956          if (valueStr.equalsIgnoreCase(s))
1957          {
1958            found = true;
1959            break;
1960          }
1961        }
1962
1963        if (! found)
1964        {
1965          throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_ALLOWED.get(
1966                                       p.getDisplayName(), valueStr));
1967        }
1968      }
1969
1970      stringValues[i] = valueStr;
1971    }
1972
1973    return stringValues;
1974  }
1975
1976
1977
1978  /**
1979   * Retrieves a list of the additional object classes (other than the base
1980   * "top" and "ds-task" classes) that should be included when creating new task
1981   * entries of this type.
1982   *
1983   * @return  A list of the additional object classes that should be included in
1984   *          new task entries of this type, or an empty list if there do not
1985   *          need to be any additional classes.
1986   */
1987  @NotNull()
1988  protected List<String> getAdditionalObjectClasses()
1989  {
1990    return Collections.emptyList();
1991  }
1992
1993
1994
1995  /**
1996   * Retrieves a list of the additional attributes (other than attributes common
1997   * to all task types) that should be included when creating new task entries
1998   * of this type.
1999   *
2000   * @return  A list of the additional attributes that should be included in new
2001   *          task entries of this type, or an empty list if there do not need
2002   *          to be any additional attributes.
2003   */
2004  @NotNull()
2005  protected List<Attribute> getAdditionalAttributes()
2006  {
2007    return Collections.emptyList();
2008  }
2009
2010
2011
2012  /**
2013   * Decodes the provided entry as a scheduled task.  An attempt will be made to
2014   * decode the entry as an appropriate subclass if possible, but it will fall
2015   * back to a generic task if it is not possible to decode as a more specific
2016   * task type.
2017   *
2018   * @param  entry  The entry to be decoded.
2019   *
2020   * @return  The decoded task.
2021   *
2022   * @throws  TaskException  If the provided entry cannot be parsed as a
2023   *                         scheduled task.
2024   */
2025  @NotNull()
2026  public static Task decodeTask(@NotNull final Entry entry)
2027         throws TaskException
2028  {
2029    final String taskClass = entry.getAttributeValue(ATTR_TASK_CLASS);
2030    if (taskClass == null)
2031    {
2032      throw new TaskException(ERR_TASK_NO_CLASS.get(entry.getDN()));
2033    }
2034
2035    try
2036    {
2037      if (taskClass.equals(AddSchemaFileTask.ADD_SCHEMA_FILE_TASK_CLASS))
2038      {
2039        return new AddSchemaFileTask(entry);
2040      }
2041      else if (taskClass.equals(AlertTask.ALERT_TASK_CLASS))
2042      {
2043        return new AlertTask(entry);
2044      }
2045      else if (taskClass.equals(AuditDataSecurityTask.
2046                    AUDIT_DATA_SECURITY_TASK_CLASS))
2047      {
2048        return new AuditDataSecurityTask(entry);
2049      }
2050      else if (taskClass.equals(BackupTask.BACKUP_TASK_CLASS))
2051      {
2052        return new BackupTask(entry);
2053      }
2054      else if (taskClass.equals(
2055           CollectSupportDataTask.COLLECT_SUPPORT_DATA_TASK_CLASS))
2056      {
2057        return new CollectSupportDataTask(entry);
2058      }
2059      else if (taskClass.equals(DelayTask.DELAY_TASK_CLASS))
2060      {
2061        return new DelayTask(entry);
2062      }
2063      else if (taskClass.equals(
2064                    DisconnectClientTask.DISCONNECT_CLIENT_TASK_CLASS))
2065      {
2066        return new DisconnectClientTask(entry);
2067      }
2068      else if (taskClass.equals(DumpDBDetailsTask.DUMP_DB_DETAILS_TASK_CLASS))
2069      {
2070        return new DumpDBDetailsTask(entry);
2071      }
2072      else if (taskClass.equals(
2073                    EnterLockdownModeTask.ENTER_LOCKDOWN_MODE_TASK_CLASS))
2074      {
2075        return new EnterLockdownModeTask(entry);
2076      }
2077      else if (taskClass.equals(ExecTask.EXEC_TASK_CLASS))
2078      {
2079        return new ExecTask(entry);
2080      }
2081      else if (taskClass.equals(ExportTask.EXPORT_TASK_CLASS))
2082      {
2083        return new ExportTask(entry);
2084      }
2085      else if (taskClass.equals(FileRetentionTask.FILE_RETENTION_TASK_CLASS))
2086      {
2087        return new FileRetentionTask(entry);
2088      }
2089      else if (taskClass.equals(
2090           GenerateServerProfileTask.GENERATE_SERVER_PROFILE_TASK_CLASS))
2091      {
2092        return new GenerateServerProfileTask(entry);
2093      }
2094      else if (taskClass.equals(GroovyScriptedTask.GROOVY_SCRIPTED_TASK_CLASS))
2095      {
2096        return new GroovyScriptedTask(entry);
2097      }
2098      else if (taskClass.equals(ImportTask.IMPORT_TASK_CLASS))
2099      {
2100        return new ImportTask(entry);
2101      }
2102      else if (taskClass.equals(
2103                    LeaveLockdownModeTask.LEAVE_LOCKDOWN_MODE_TASK_CLASS))
2104      {
2105        return new LeaveLockdownModeTask(entry);
2106      }
2107      else if (taskClass.equals(
2108                    PopulateComposedAttributeValuesTask.
2109                         POPULATE_COMPOSED_ATTRIBUTE_VALUES_TASK_CLASS))
2110      {
2111        return new PopulateComposedAttributeValuesTask(entry);
2112      }
2113      else if (taskClass.equals(RebuildTask.REBUILD_TASK_CLASS))
2114      {
2115        return new RebuildTask(entry);
2116      }
2117      else if (taskClass.equals(
2118                    ReEncodeEntriesTask.RE_ENCODE_ENTRIES_TASK_CLASS))
2119      {
2120        return new ReEncodeEntriesTask(entry);
2121      }
2122      else if (taskClass.equals(RefreshEncryptionSettingsTask.
2123                    REFRESH_ENCRYPTION_SETTINGS_TASK_CLASS))
2124      {
2125        return new RefreshEncryptionSettingsTask(entry);
2126      }
2127      else if (taskClass.equals(
2128           ReloadGlobalIndexTask.RELOAD_GLOBAL_INDEX_TASK_CLASS))
2129      {
2130        return new ReloadGlobalIndexTask(entry);
2131      }
2132      else if (taskClass.equals(
2133           ReloadHTTPConnectionHandlerCertificatesTask.
2134                RELOAD_HTTP_CONNECTION_HANDLER_CERTIFICATES_TASK_CLASS))
2135      {
2136        return new ReloadHTTPConnectionHandlerCertificatesTask(entry);
2137      }
2138      else if (taskClass.equals(
2139           RemoveAttributeTypeTask.REMOVE_ATTRIBUTE_TYPE_TASK_CLASS))
2140      {
2141        return new RemoveAttributeTypeTask(entry);
2142      }
2143      else if (taskClass.equals(
2144           RemoveObjectClassTask.REMOVE_OBJECT_CLASS_TASK_CLASS))
2145      {
2146        return new RemoveObjectClassTask(entry);
2147      }
2148      else if (taskClass.equals(RestoreTask.RESTORE_TASK_CLASS))
2149      {
2150        return new RestoreTask(entry);
2151      }
2152      else if (taskClass.equals(RotateLogTask.ROTATE_LOG_TASK_CLASS))
2153      {
2154        return new RotateLogTask(entry);
2155      }
2156      else if (taskClass.equals(SearchTask.SEARCH_TASK_CLASS))
2157      {
2158        return new SearchTask(entry);
2159      }
2160      else if (taskClass.equals(ShutdownTask.SHUTDOWN_TASK_CLASS))
2161      {
2162        return new ShutdownTask(entry);
2163      }
2164      else if (taskClass.equals(SynchronizeEncryptionSettingsTask.
2165                    SYNCHRONIZE_ENCRYPTION_SETTINGS_TASK_CLASS))
2166      {
2167        return new SynchronizeEncryptionSettingsTask(entry);
2168      }
2169      else if (taskClass.equals(ThirdPartyTask.THIRD_PARTY_TASK_CLASS))
2170      {
2171        return new ThirdPartyTask(entry);
2172      }
2173    }
2174    catch (final TaskException te)
2175    {
2176      Debug.debugException(te);
2177    }
2178
2179    return new Task(entry);
2180  }
2181
2182
2183
2184  /**
2185   * Retrieves a list of task properties that may be provided when scheduling
2186   * any type of task.  This includes:
2187   * <UL>
2188   *   <LI>The task ID</LI>
2189   *   <LI>The scheduled start time</LI>
2190   *   <LI>The task IDs of any tasks on which this task is dependent</LI>
2191   *   <LI>The action to take for this task if any of its dependencies fail</LI>
2192   *   <LI>The addresses of users to notify when this task starts</LI>
2193   *   <LI>The addresses of users to notify when this task completes</LI>
2194   *   <LI>The addresses of users to notify if this task succeeds</LI>
2195   *   <LI>The addresses of users to notify if this task fails</LI>
2196   *   <LI>A flag indicating whether to generate an alert when the task
2197   *       starts</LI>
2198   *   <LI>A flag indicating whether to generate an alert when the task
2199   *       succeeds</LI>
2200   *   <LI>A flag indicating whether to generate an alert when the task
2201   *       fails</LI>
2202   * </UL>
2203   *
2204   * @return  A list of task properties that may be provided when scheduling any
2205   *          type of task.
2206   */
2207  @NotNull()
2208  public static List<TaskProperty> getCommonTaskProperties()
2209  {
2210    final List<TaskProperty> taskList = Arrays.asList(
2211         PROPERTY_TASK_ID,
2212         PROPERTY_SCHEDULED_START_TIME,
2213         PROPERTY_DEPENDENCY_ID,
2214         PROPERTY_FAILED_DEPENDENCY_ACTION,
2215         PROPERTY_NOTIFY_ON_START,
2216         PROPERTY_NOTIFY_ON_COMPLETION,
2217         PROPERTY_NOTIFY_ON_SUCCESS,
2218         PROPERTY_NOTIFY_ON_ERROR,
2219         PROPERTY_ALERT_ON_START,
2220         PROPERTY_ALERT_ON_SUCCESS,
2221         PROPERTY_ALERT_ON_ERROR);
2222
2223    return Collections.unmodifiableList(taskList);
2224  }
2225
2226
2227
2228  /**
2229   * Retrieves a list of task-specific properties that may be provided when
2230   * scheduling a task of this type.  This method should be overridden by
2231   * subclasses in order to provide an appropriate set of properties.
2232   *
2233   * @return  A list of task-specific properties that may be provided when
2234   *          scheduling a task of this type.
2235   */
2236  @NotNull()
2237  public List<TaskProperty> getTaskSpecificProperties()
2238  {
2239    return Collections.emptyList();
2240  }
2241
2242
2243
2244  /**
2245   * Retrieves the values of the task properties for this task.  The data type
2246   * of the values will vary based on the data type of the corresponding task
2247   * property and may be one of the following types:  {@code Boolean},
2248   * {@code Date}, {@code Long}, or {@code String}.  Task properties which do
2249   * not have any values will be included in the map with an empty value list.
2250   * <BR><BR>
2251   * Note that subclasses which have additional task properties should override
2252   * this method and return a map which contains both the property values from
2253   * this class (obtained from {@code super.getTaskPropertyValues()} and the
2254   * values of their own task-specific properties.
2255   *
2256   * @return  A map of the task property values for this task.
2257   */
2258  @NotNull()
2259  public Map<TaskProperty,List<Object>> getTaskPropertyValues()
2260  {
2261    final LinkedHashMap<TaskProperty,List<Object>> props =
2262         new LinkedHashMap<>(StaticUtils.computeMapCapacity(20));
2263
2264    props.put(PROPERTY_TASK_ID,
2265              Collections.<Object>singletonList(taskID));
2266
2267    if (scheduledStartTime == null)
2268    {
2269      props.put(PROPERTY_SCHEDULED_START_TIME, Collections.emptyList());
2270    }
2271    else
2272    {
2273      props.put(PROPERTY_SCHEDULED_START_TIME,
2274                Collections.<Object>singletonList(scheduledStartTime));
2275    }
2276
2277    props.put(PROPERTY_DEPENDENCY_ID,
2278              Collections.<Object>unmodifiableList(dependencyIDs));
2279
2280    if (failedDependencyAction == null)
2281    {
2282      props.put(PROPERTY_FAILED_DEPENDENCY_ACTION, Collections.emptyList());
2283    }
2284    else
2285    {
2286      props.put(PROPERTY_FAILED_DEPENDENCY_ACTION,
2287           Collections.<Object>singletonList(failedDependencyAction.getName()));
2288    }
2289
2290    props.put(PROPERTY_NOTIFY_ON_START,
2291              Collections.<Object>unmodifiableList(notifyOnStart));
2292
2293    props.put(PROPERTY_NOTIFY_ON_COMPLETION,
2294              Collections.<Object>unmodifiableList(notifyOnCompletion));
2295
2296    props.put(PROPERTY_NOTIFY_ON_SUCCESS,
2297              Collections.<Object>unmodifiableList(notifyOnSuccess));
2298
2299    props.put(PROPERTY_NOTIFY_ON_ERROR,
2300              Collections.<Object>unmodifiableList(notifyOnError));
2301
2302    if (alertOnStart != null)
2303    {
2304      props.put(PROPERTY_ALERT_ON_START,
2305           Collections.<Object>singletonList(alertOnStart));
2306    }
2307
2308    if (alertOnSuccess != null)
2309    {
2310      props.put(PROPERTY_ALERT_ON_SUCCESS,
2311           Collections.<Object>singletonList(alertOnSuccess));
2312    }
2313
2314    if (alertOnError!= null)
2315    {
2316      props.put(PROPERTY_ALERT_ON_ERROR,
2317           Collections.<Object>singletonList(alertOnError));
2318    }
2319
2320    return Collections.unmodifiableMap(props);
2321  }
2322
2323
2324
2325  /**
2326   * Retrieves a string representation of this task.
2327   *
2328   * @return  A string representation of this task.
2329   */
2330  @Override()
2331  @NotNull()
2332  public final String toString()
2333  {
2334    final StringBuilder buffer = new StringBuilder();
2335    toString(buffer);
2336    return buffer.toString();
2337  }
2338
2339
2340
2341  /**
2342   * Appends a string representation of this task to the provided buffer.
2343   *
2344   * @param  buffer  The buffer to which the string representation should be
2345   *                 provided.
2346   */
2347  public final void toString(@NotNull final StringBuilder buffer)
2348  {
2349    buffer.append("Task(name='");
2350    buffer.append(getTaskName());
2351    buffer.append("', className='");
2352    buffer.append(taskClassName);
2353    buffer.append(", properties={");
2354
2355    boolean added = false;
2356    for (final Map.Entry<TaskProperty,List<Object>> e :
2357         getTaskPropertyValues().entrySet())
2358    {
2359      if (added)
2360      {
2361        buffer.append(", ");
2362      }
2363      else
2364      {
2365        added = true;
2366      }
2367
2368      buffer.append(e.getKey().getAttributeName());
2369      buffer.append("={");
2370
2371      final Iterator<Object> iterator = e.getValue().iterator();
2372      while (iterator.hasNext())
2373      {
2374        buffer.append('\'');
2375        buffer.append(String.valueOf(iterator.next()));
2376        buffer.append('\'');
2377
2378        if (iterator.hasNext())
2379        {
2380          buffer.append(',');
2381        }
2382      }
2383
2384      buffer.append('}');
2385    }
2386
2387    buffer.append("})");
2388  }
2389}