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