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