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