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 RestoreTask(),
732         new RotateLogTask(),
733         new SearchTask(),
734         new ShutdownTask(),
735         new SynchronizeEncryptionSettingsTask(),
736         new ThirdPartyTask());
737
738    return Collections.unmodifiableList(taskList);
739  }
740
741
742
743  /**
744   * Retrieves a human-readable name for this task.
745   *
746   * @return  A human-readable name for this task.
747   */
748  public String getTaskName()
749  {
750    return INFO_TASK_NAME_GENERIC.get();
751  }
752
753
754
755  /**
756   * Retrieves a human-readable description for this task.
757   *
758   * @return  A human-readable description for this task.
759   */
760  public String getTaskDescription()
761  {
762    return INFO_TASK_DESCRIPTION_GENERIC.get();
763  }
764
765
766
767  /**
768   * Retrieves the entry from which this task was decoded, if available.  Note
769   * that although the entry is not immutable, changes made to it will not be
770   * reflected in this task.
771   *
772   * @return  The entry from which this task was decoded, or {@code null} if
773   *          this task was not created from an existing entry.
774   */
775  protected final Entry getTaskEntry()
776  {
777    return taskEntry;
778  }
779
780
781
782  /**
783   * Retrieves the DN of the entry in which this scheduled task is defined.
784   *
785   * @return  The DN of the entry in which this scheduled task is defined.
786   */
787  public final String getTaskEntryDN()
788  {
789    return taskEntryDN;
790  }
791
792
793
794  /**
795   * Retrieves the task ID for this task.
796   *
797   * @return  The task ID for this task.
798   */
799  public final String getTaskID()
800  {
801    return taskID;
802  }
803
804
805
806  /**
807   * Retrieves the fully-qualified name of the Java class that provides the
808   * logic for this class.
809   *
810   * @return  The fully-qualified name of the Java class that provides the logic
811   *          for this task.
812   */
813  public final String getTaskClassName()
814  {
815    return taskClassName;
816  }
817
818
819
820  /**
821   * Retrieves the current state for this task.
822   *
823   * @return  The current state for this task.
824   */
825  public final TaskState getState()
826  {
827    return taskState;
828  }
829
830
831
832  /**
833   * Indicates whether this task is currently pending execution.
834   *
835   * @return  {@code true} if this task is currently pending execution, or
836   *          {@code false} if not.
837   */
838  public final boolean isPending()
839  {
840    return taskState.isPending();
841  }
842
843
844
845  /**
846   * Indicates whether this task is currently running.
847   *
848   * @return  {@code true} if this task is currently running, or {@code false}
849   *          if not.
850   */
851  public final boolean isRunning()
852  {
853    return taskState.isRunning();
854  }
855
856
857
858  /**
859   * Indicates whether this task has completed execution.
860   *
861   * @return  {@code true} if this task has completed execution, or
862   *          {@code false} if not.
863   */
864  public final boolean isCompleted()
865  {
866    return taskState.isCompleted();
867  }
868
869
870
871  /**
872   * Retrieves the time that this task is/was scheduled to start running.
873   *
874   * @return  The time that this task is/was scheduled to start running, or
875   *          {@code null} if that is not available and therefore the task
876   *          should start running as soon as all dependencies have been met.
877   */
878  public final Date getScheduledStartTime()
879  {
880    return scheduledStartTime;
881  }
882
883
884
885  /**
886   * Retrieves the time that this task actually started running.
887   *
888   * @return  The time that this task actually started running, or {@code null}
889   *          if that is not available (e.g., because the task has not yet
890   *          started).
891   */
892  public final Date getActualStartTime()
893  {
894    return actualStartTime;
895  }
896
897
898
899  /**
900   * Retrieves the time that this task completed.
901   *
902   * @return  The time that this task completed, or {@code null} if it has not
903   *          yet completed.
904   */
905  public final Date getCompletionTime()
906  {
907    return completionTime;
908  }
909
910
911
912  /**
913   * Retrieves a list of the task IDs for tasks that must complete before this
914   * task will be eligible to start.
915   *
916   * @return  A list of the task IDs for tasks that must complete before this
917   *          task will be eligible to start, or an empty list if this task does
918   *          not have any dependencies.
919   */
920  public final List<String> getDependencyIDs()
921  {
922    return dependencyIDs;
923  }
924
925
926
927  /**
928   * Retrieves the failed dependency action for this task, which indicates the
929   * behavior that it should exhibit if any of its dependencies encounter a
930   * failure.
931   *
932   * @return  The failed dependency action for this task, or {@code null} if it
933   *          is not available.
934   */
935  public final FailedDependencyAction getFailedDependencyAction()
936  {
937    return failedDependencyAction;
938  }
939
940
941
942  /**
943   * Retrieves the log messages for this task.  Note that if the task has
944   * generated a very large number of log messages, then only a portion of the
945   * most recent messages may be available.
946   *
947   * @return  The log messages for this task, or an empty list if this task does
948   *          not have any log messages.
949   */
950  public final List<String> getLogMessages()
951  {
952    return logMessages;
953  }
954
955
956
957  /**
958   * Retrieves a list of the e-mail addresses of the individuals that should be
959   * notified whenever this task completes processing, regardless of whether it
960   * was successful.
961   *
962   * @return  A list of the e-mail addresses of the individuals that should be
963   *          notified whenever this task completes processing, or an empty list
964   *          if there are none.
965   */
966  public final List<String> getNotifyOnCompletionAddresses()
967  {
968    return notifyOnCompletion;
969  }
970
971
972
973  /**
974   * Retrieves a list of the e-mail addresses of the individuals that should be
975   * notified if this task stops processing prematurely due to an error or
976   * other external action (e.g., server shutdown or administrative cancel).
977   *
978   * @return  A list of the e-mail addresses of the individuals that should be
979   *          notified if this task stops processing prematurely, or an empty
980   *          list if there are none.
981   */
982  public final List<String> getNotifyOnErrorAddresses()
983  {
984    return notifyOnError;
985  }
986
987
988
989  /**
990   * Creates an entry that may be added to the Directory Server to create a new
991   * instance of this task.
992   *
993   * @return  An entry that may be added to the Directory Server to create a new
994   *          instance of this task.
995   */
996  public final Entry createTaskEntry()
997  {
998    final ArrayList<Attribute> attributes = new ArrayList<Attribute>();
999
1000    final ArrayList<String> ocValues = new ArrayList<String>(5);
1001    ocValues.add("top");
1002    ocValues.add(OC_TASK);
1003    ocValues.addAll(getAdditionalObjectClasses());
1004    attributes.add(new Attribute("objectClass", ocValues));
1005
1006    attributes.add(new Attribute(ATTR_TASK_ID, taskID));
1007
1008    attributes.add(new Attribute(ATTR_TASK_CLASS, taskClassName));
1009
1010    if (scheduledStartTime != null)
1011    {
1012      attributes.add(new Attribute(ATTR_SCHEDULED_START_TIME,
1013                                   encodeGeneralizedTime(scheduledStartTime)));
1014    }
1015
1016    if (! dependencyIDs.isEmpty())
1017    {
1018      attributes.add(new Attribute(ATTR_DEPENDENCY_ID, dependencyIDs));
1019    }
1020
1021    if (failedDependencyAction != null)
1022    {
1023      attributes.add(new Attribute(ATTR_FAILED_DEPENDENCY_ACTION,
1024                                   failedDependencyAction.getName()));
1025    }
1026
1027    if (! notifyOnCompletion.isEmpty())
1028    {
1029      attributes.add(new Attribute(ATTR_NOTIFY_ON_COMPLETION,
1030                                   notifyOnCompletion));
1031    }
1032
1033    if (! notifyOnError.isEmpty())
1034    {
1035      attributes.add(new Attribute(ATTR_NOTIFY_ON_ERROR, notifyOnError));
1036    }
1037
1038    attributes.addAll(getAdditionalAttributes());
1039
1040    return new Entry(taskEntryDN, attributes);
1041  }
1042
1043
1044
1045  /**
1046   * Parses the value of the specified attribute as a {@code boolean} value, or
1047   * throws an exception if the value cannot be decoded as a boolean.
1048   *
1049   * @param  taskEntry      The entry containing the attribute to be parsed.
1050   * @param  attributeName  The name of the attribute from which the value was
1051   *                        taken.
1052   * @param  defaultValue   The default value to use if the provided value
1053   *                        string is {@code null}.
1054   *
1055   * @return  {@code true} if the value string represents a boolean value of
1056   *          {@code true}, {@code false} if the value string represents a
1057   *          boolean value of {@code false}, or the default value if the value
1058   *          string is {@code null}.
1059   *
1060   * @throws  TaskException  If the provided value string cannot be parsed as a
1061   *                         {@code boolean} value.
1062   */
1063  protected static boolean parseBooleanValue(final Entry taskEntry,
1064                                             final String attributeName,
1065                                             final boolean defaultValue)
1066            throws TaskException
1067  {
1068    final String valueString = taskEntry.getAttributeValue(attributeName);
1069    if (valueString == null)
1070    {
1071      return defaultValue;
1072    }
1073    else if (valueString.equalsIgnoreCase("true"))
1074    {
1075      return true;
1076    }
1077    else if (valueString.equalsIgnoreCase("false"))
1078    {
1079      return false;
1080    }
1081    else
1082    {
1083      throw new TaskException(ERR_TASK_CANNOT_PARSE_BOOLEAN.get(
1084                                   taskEntry.getDN(), valueString,
1085                                   attributeName));
1086    }
1087  }
1088
1089
1090
1091  /**
1092   * Parses the values of the specified attribute as a list of strings.
1093   *
1094   * @param  taskEntry      The entry containing the attribute to be parsed.
1095   * @param  attributeName  The name of the attribute from which the value was
1096   *                        taken.
1097   *
1098   * @return  A list of strings containing the values of the specified
1099   *          attribute, or an empty list if the specified attribute does not
1100   *          exist in the target entry.  The returned list will be
1101   *          unmodifiable.
1102   */
1103  protected static List<String> parseStringList(final Entry taskEntry,
1104                                                final String attributeName)
1105  {
1106    final String[] valueStrings = taskEntry.getAttributeValues(attributeName);
1107    if (valueStrings == null)
1108    {
1109      return Collections.emptyList();
1110    }
1111    else
1112    {
1113      return Collections.unmodifiableList(Arrays.asList(valueStrings));
1114    }
1115  }
1116
1117
1118
1119  /**
1120   * Parses the provided set of values for the associated task property as a
1121   * {@code Boolean}.
1122   *
1123   * @param  p             The task property with which the values are
1124   *                       associated.
1125   * @param  values        The provided values for the task property.
1126   * @param  defaultValue  The default value to use if the provided object array
1127   *                       is empty.
1128   *
1129   * @return  The parsed {@code Boolean} value.
1130   *
1131   * @throws  TaskException  If there is a problem with the provided values.
1132   */
1133  protected static Boolean parseBoolean(final TaskProperty p,
1134                                        final List<Object> values,
1135                                        final Boolean defaultValue)
1136            throws TaskException
1137  {
1138    // Check to see if any values were provided.  If not, then it may or may not
1139    // be a problem.
1140    if (values.isEmpty())
1141    {
1142      if (p.isRequired())
1143      {
1144        throw new TaskException(ERR_TASK_REQUIRED_PROPERTY_WITHOUT_VALUES.get(
1145                                     p.getDisplayName()));
1146      }
1147      else
1148      {
1149        return defaultValue;
1150      }
1151    }
1152
1153    // If there were multiple values, then that's always an error.
1154    if (values.size() > 1)
1155    {
1156      throw new TaskException(ERR_TASK_PROPERTY_NOT_MULTIVALUED.get(
1157                                   p.getDisplayName()));
1158    }
1159
1160    // Make sure that the value can be interpreted as a Boolean.
1161    final Boolean booleanValue;
1162    final Object o = values.get(0);
1163    if (o instanceof Boolean)
1164    {
1165      booleanValue = (Boolean) o;
1166    }
1167    else if (o instanceof String)
1168    {
1169      final String valueStr = (String) o;
1170      if (valueStr.equalsIgnoreCase("true"))
1171      {
1172        booleanValue = Boolean.TRUE;
1173      }
1174      else if (valueStr.equalsIgnoreCase("false"))
1175      {
1176        booleanValue = Boolean.FALSE;
1177      }
1178      else
1179      {
1180        throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_BOOLEAN.get(
1181                                     p.getDisplayName()));
1182      }
1183    }
1184    else
1185    {
1186      throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_BOOLEAN.get(
1187                                   p.getDisplayName()));
1188    }
1189
1190    return booleanValue;
1191  }
1192
1193
1194
1195  /**
1196   * Parses the provided set of values for the associated task property as a
1197   * {@code Date}.
1198   *
1199   * @param  p             The task property with which the values are
1200   *                       associated.
1201   * @param  values        The provided values for the task property.
1202   * @param  defaultValue  The default value to use if the provided object array
1203   *                       is empty.
1204   *
1205   * @return  The parsed {@code Date} value.
1206   *
1207   * @throws  TaskException  If there is a problem with the provided values.
1208   */
1209  protected static Date parseDate(final TaskProperty p,
1210                                  final List<Object> values,
1211                                  final Date defaultValue)
1212            throws TaskException
1213  {
1214    // Check to see if any values were provided.  If not, then it may or may not
1215    // be a problem.
1216    if (values.isEmpty())
1217    {
1218      if (p.isRequired())
1219      {
1220        throw new TaskException(ERR_TASK_REQUIRED_PROPERTY_WITHOUT_VALUES.get(
1221                                     p.getDisplayName()));
1222      }
1223      else
1224      {
1225        return defaultValue;
1226      }
1227    }
1228
1229    // If there were multiple values, then that's always an error.
1230    if (values.size() > 1)
1231    {
1232      throw new TaskException(ERR_TASK_PROPERTY_NOT_MULTIVALUED.get(
1233                                   p.getDisplayName()));
1234    }
1235
1236    // Make sure that the value can be interpreted as a Date.
1237    final Date dateValue;
1238    final Object o = values.get(0);
1239    if (o instanceof Date)
1240    {
1241      dateValue = (Date) o;
1242    }
1243    else if (o instanceof String)
1244    {
1245      try
1246      {
1247        dateValue = decodeGeneralizedTime((String) o);
1248      }
1249      catch (final ParseException pe)
1250      {
1251        throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_DATE.get(
1252                                     p.getDisplayName()), pe);
1253      }
1254    }
1255    else
1256    {
1257      throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_DATE.get(
1258                                   p.getDisplayName()));
1259    }
1260
1261    // If the task property has a set of allowed values, then make sure that the
1262    // provided value is acceptable.
1263    final Object[] allowedValues = p.getAllowedValues();
1264    if (allowedValues != null)
1265    {
1266      boolean found = false;
1267      for (final Object allowedValue : allowedValues)
1268      {
1269        if (dateValue.equals(allowedValue))
1270        {
1271          found = true;
1272          break;
1273        }
1274      }
1275
1276      if (! found)
1277      {
1278        throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_ALLOWED.get(
1279                                     p.getDisplayName(), dateValue.toString()));
1280      }
1281    }
1282
1283    return dateValue;
1284  }
1285
1286
1287
1288  /**
1289   * Parses the provided set of values for the associated task property as a
1290   * {@code Long}.
1291   *
1292   * @param  p             The task property with which the values are
1293   *                       associated.
1294   * @param  values        The provided values for the task property.
1295   * @param  defaultValue  The default value to use if the provided object array
1296   *                       is empty.
1297   *
1298   * @return  The parsed {@code Long} value.
1299   *
1300   * @throws  TaskException  If there is a problem with the provided values.
1301   */
1302  protected static Long parseLong(final TaskProperty p,
1303                                  final List<Object> values,
1304                                  final Long defaultValue)
1305            throws TaskException
1306  {
1307    // Check to see if any values were provided.  If not, then it may or may not
1308    // be a problem.
1309    if (values.isEmpty())
1310    {
1311      if (p.isRequired())
1312      {
1313        throw new TaskException(ERR_TASK_REQUIRED_PROPERTY_WITHOUT_VALUES.get(
1314                                     p.getDisplayName()));
1315      }
1316      else
1317      {
1318        return defaultValue;
1319      }
1320    }
1321
1322    // If there were multiple values, then that's always an error.
1323    if (values.size() > 1)
1324    {
1325      throw new TaskException(ERR_TASK_PROPERTY_NOT_MULTIVALUED.get(
1326                                   p.getDisplayName()));
1327    }
1328
1329    // Make sure that the value can be interpreted as a Long.
1330    final Long longValue;
1331    final Object o = values.get(0);
1332    if (o instanceof Long)
1333    {
1334      longValue = (Long) o;
1335    }
1336    else if (o instanceof Number)
1337    {
1338      longValue = ((Number) o).longValue();
1339    }
1340    else if (o instanceof String)
1341    {
1342      try
1343      {
1344        longValue = Long.parseLong((String) o);
1345      }
1346      catch (final Exception e)
1347      {
1348        throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_LONG.get(
1349                                     p.getDisplayName()), e);
1350      }
1351    }
1352    else
1353    {
1354      throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_LONG.get(
1355                                   p.getDisplayName()));
1356    }
1357
1358    // If the task property has a set of allowed values, then make sure that the
1359    // provided value is acceptable.
1360    final Object[] allowedValues = p.getAllowedValues();
1361    if (allowedValues != null)
1362    {
1363      boolean found = false;
1364      for (final Object allowedValue : allowedValues)
1365      {
1366        if (longValue.equals(allowedValue))
1367        {
1368          found = true;
1369          break;
1370        }
1371      }
1372
1373      if (! found)
1374      {
1375        throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_ALLOWED.get(
1376                                     p.getDisplayName(), longValue.toString()));
1377      }
1378    }
1379
1380    return longValue;
1381  }
1382
1383
1384
1385  /**
1386   * Parses the provided set of values for the associated task property as a
1387   * {@code String}.
1388   *
1389   * @param  p             The task property with which the values are
1390   *                       associated.
1391   * @param  values        The provided values for the task property.
1392   * @param  defaultValue  The default value to use if the provided object array
1393   *                       is empty.
1394   *
1395   * @return  The parsed {@code String} value.
1396   *
1397   * @throws  TaskException  If there is a problem with the provided values.
1398   */
1399  protected static String parseString(final TaskProperty p,
1400                                      final List<Object> values,
1401                                      final String defaultValue)
1402            throws TaskException
1403  {
1404    // Check to see if any values were provided.  If not, then it may or may not
1405    // be a problem.
1406    if (values.isEmpty())
1407    {
1408      if (p.isRequired())
1409      {
1410        throw new TaskException(ERR_TASK_REQUIRED_PROPERTY_WITHOUT_VALUES.get(
1411                                     p.getDisplayName()));
1412      }
1413      else
1414      {
1415        return defaultValue;
1416      }
1417    }
1418
1419    // If there were multiple values, then that's always an error.
1420    if (values.size() > 1)
1421    {
1422      throw new TaskException(ERR_TASK_PROPERTY_NOT_MULTIVALUED.get(
1423                                   p.getDisplayName()));
1424    }
1425
1426    // Make sure that the value is a String.
1427    final String valueStr;
1428    final Object o = values.get(0);
1429    if (o instanceof String)
1430    {
1431      valueStr = (String) o;
1432    }
1433    else if (values.get(0) instanceof CharSequence)
1434    {
1435      valueStr = o.toString();
1436    }
1437    else
1438    {
1439      throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_STRING.get(
1440                                   p.getDisplayName()));
1441    }
1442
1443    // If the task property has a set of allowed values, then make sure that the
1444    // provided value is acceptable.
1445    final Object[] allowedValues = p.getAllowedValues();
1446    if (allowedValues != null)
1447    {
1448      boolean found = false;
1449      for (final Object allowedValue : allowedValues)
1450      {
1451        final String s = (String) allowedValue;
1452        if (valueStr.equalsIgnoreCase(s))
1453        {
1454          found = true;
1455          break;
1456        }
1457      }
1458
1459      if (! found)
1460      {
1461        throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_ALLOWED.get(
1462                                     p.getDisplayName(), valueStr));
1463      }
1464    }
1465
1466    return valueStr;
1467  }
1468
1469
1470
1471  /**
1472   * Parses the provided set of values for the associated task property as a
1473   * {@code String} array.
1474   *
1475   * @param  p              The task property with which the values are
1476   *                        associated.
1477   * @param  values         The provided values for the task property.
1478   * @param  defaultValues  The set of default values to use if the provided
1479   *                        object array is empty.
1480   *
1481   * @return  The parsed {@code String} values.
1482   *
1483   * @throws  TaskException  If there is a problem with the provided values.
1484   */
1485  protected static String[] parseStrings(final TaskProperty p,
1486                                         final List<Object> values,
1487                                         final String[] defaultValues)
1488            throws TaskException
1489  {
1490    // Check to see if any values were provided.  If not, then it may or may not
1491    // be a problem.
1492    if (values.isEmpty())
1493    {
1494      if (p.isRequired())
1495      {
1496        throw new TaskException(ERR_TASK_REQUIRED_PROPERTY_WITHOUT_VALUES.get(
1497                                     p.getDisplayName()));
1498      }
1499      else
1500      {
1501        return defaultValues;
1502      }
1503    }
1504
1505
1506    // Iterate through each of the values and perform appropriate validation for
1507    // them.
1508    final String[] stringValues = new String[values.size()];
1509    for (int i=0; i < values.size(); i++)
1510    {
1511      final Object o = values.get(i);
1512
1513      // Make sure that the value is a String.
1514      final String valueStr;
1515      if (o instanceof String)
1516      {
1517        valueStr = (String) o;
1518      }
1519      else if (o instanceof CharSequence)
1520      {
1521        valueStr = o.toString();
1522      }
1523      else
1524      {
1525        throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_STRING.get(
1526                                     p.getDisplayName()));
1527      }
1528
1529      // If the task property has a set of allowed values, then make sure that
1530      // the provided value is acceptable.
1531      final Object[] allowedValues = p.getAllowedValues();
1532      if (allowedValues != null)
1533      {
1534        boolean found = false;
1535        for (final Object allowedValue : allowedValues)
1536        {
1537          final String s = (String) allowedValue;
1538          if (valueStr.equalsIgnoreCase(s))
1539          {
1540            found = true;
1541            break;
1542          }
1543        }
1544
1545        if (! found)
1546        {
1547          throw new TaskException(ERR_TASK_PROPERTY_VALUE_NOT_ALLOWED.get(
1548                                       p.getDisplayName(), valueStr));
1549        }
1550      }
1551
1552      stringValues[i] = valueStr;
1553    }
1554
1555    return stringValues;
1556  }
1557
1558
1559
1560  /**
1561   * Retrieves a list of the additional object classes (other than the base
1562   * "top" and "ds-task" classes) that should be included when creating new task
1563   * entries of this type.
1564   *
1565   * @return  A list of the additional object classes that should be included in
1566   *          new task entries of this type, or an empty list if there do not
1567   *          need to be any additional classes.
1568   */
1569  protected List<String> getAdditionalObjectClasses()
1570  {
1571    return Collections.emptyList();
1572  }
1573
1574
1575
1576  /**
1577   * Retrieves a list of the additional attributes (other than attributes common
1578   * to all task types) that should be included when creating new task entries
1579   * of this type.
1580   *
1581   * @return  A list of the additional attributes that should be included in new
1582   *          task entries of this type, or an empty list if there do not need
1583   *          to be any additional attributes.
1584   */
1585  protected List<Attribute> getAdditionalAttributes()
1586  {
1587    return Collections.emptyList();
1588  }
1589
1590
1591
1592  /**
1593   * Decodes the provided entry as a scheduled task.  An attempt will be made to
1594   * decode the entry as an appropriate subclass if possible, but it will fall
1595   * back to a generic task if it is not possible to decode as a more specific
1596   * task type.
1597   *
1598   * @param  entry  The entry to be decoded.
1599   *
1600   * @return  The decoded task.
1601   *
1602   * @throws  TaskException  If the provided entry cannot be parsed as a
1603   *                         scheduled task.
1604   */
1605  public static Task decodeTask(final Entry entry)
1606         throws TaskException
1607  {
1608    final String taskClass = entry.getAttributeValue(ATTR_TASK_CLASS);
1609    if (taskClass == null)
1610    {
1611      throw new TaskException(ERR_TASK_NO_CLASS.get(entry.getDN()));
1612    }
1613
1614    try
1615    {
1616      if (taskClass.equals(AddSchemaFileTask.ADD_SCHEMA_FILE_TASK_CLASS))
1617      {
1618        return new AddSchemaFileTask(entry);
1619      }
1620      else if (taskClass.equals(AlertTask.ALERT_TASK_CLASS))
1621      {
1622        return new AlertTask(entry);
1623      }
1624      else if (taskClass.equals(AuditDataSecurityTask.
1625                    AUDIT_DATA_SECURITY_TASK_CLASS))
1626      {
1627        return new AuditDataSecurityTask(entry);
1628      }
1629      else if (taskClass.equals(BackupTask.BACKUP_TASK_CLASS))
1630      {
1631        return new BackupTask(entry);
1632      }
1633      else if (taskClass.equals(
1634                    DisconnectClientTask.DISCONNECT_CLIENT_TASK_CLASS))
1635      {
1636        return new DisconnectClientTask(entry);
1637      }
1638      else if (taskClass.equals(DumpDBDetailsTask.DUMP_DB_DETAILS_TASK_CLASS))
1639      {
1640        return new DumpDBDetailsTask(entry);
1641      }
1642      else if (taskClass.equals(
1643                    EnterLockdownModeTask.ENTER_LOCKDOWN_MODE_TASK_CLASS))
1644      {
1645        return new EnterLockdownModeTask(entry);
1646      }
1647      else if (taskClass.equals(ExportTask.EXPORT_TASK_CLASS))
1648      {
1649        return new ExportTask(entry);
1650      }
1651      else if (taskClass.equals(GroovyScriptedTask.GROOVY_SCRIPTED_TASK_CLASS))
1652      {
1653        return new GroovyScriptedTask(entry);
1654      }
1655      else if (taskClass.equals(ImportTask.IMPORT_TASK_CLASS))
1656      {
1657        return new ImportTask(entry);
1658      }
1659      else if (taskClass.equals(
1660                    LeaveLockdownModeTask.LEAVE_LOCKDOWN_MODE_TASK_CLASS))
1661      {
1662        return new LeaveLockdownModeTask(entry);
1663      }
1664      else if (taskClass.equals(RebuildTask.REBUILD_TASK_CLASS))
1665      {
1666        return new RebuildTask(entry);
1667      }
1668      else if (taskClass.equals(
1669                    ReEncodeEntriesTask.RE_ENCODE_ENTRIES_TASK_CLASS))
1670      {
1671        return new ReEncodeEntriesTask(entry);
1672      }
1673      else if (taskClass.equals(RefreshEncryptionSettingsTask.
1674                    REFRESH_ENCRYPTION_SETTINGS_TASK_CLASS))
1675      {
1676        return new RefreshEncryptionSettingsTask(entry);
1677      }
1678      else if (taskClass.equals(
1679           ReloadGlobalIndexTask.RELOAD_GLOBAL_INDEX_TASK_CLASS))
1680      {
1681        return new ReloadGlobalIndexTask(entry);
1682      }
1683      else if (taskClass.equals(RestoreTask.RESTORE_TASK_CLASS))
1684      {
1685        return new RestoreTask(entry);
1686      }
1687      else if (taskClass.equals(RotateLogTask.ROTATE_LOG_TASK_CLASS))
1688      {
1689        return new RotateLogTask(entry);
1690      }
1691      else if (taskClass.equals(SearchTask.SEARCH_TASK_CLASS))
1692      {
1693        return new SearchTask(entry);
1694      }
1695      else if (taskClass.equals(ShutdownTask.SHUTDOWN_TASK_CLASS))
1696      {
1697        return new ShutdownTask(entry);
1698      }
1699      else if (taskClass.equals(SynchronizeEncryptionSettingsTask.
1700                    SYNCHRONIZE_ENCRYPTION_SETTINGS_TASK_CLASS))
1701      {
1702        return new SynchronizeEncryptionSettingsTask(entry);
1703      }
1704      else if (taskClass.equals(ThirdPartyTask.THIRD_PARTY_TASK_CLASS))
1705      {
1706        return new ThirdPartyTask(entry);
1707      }
1708    }
1709    catch (final TaskException te)
1710    {
1711      debugException(te);
1712    }
1713
1714    return new Task(entry);
1715  }
1716
1717
1718
1719  /**
1720   * Retrieves a list of task properties that may be provided when scheduling
1721   * any type of task.  This includes:
1722   * <UL>
1723   *   <LI>The task ID</LI>
1724   *   <LI>The scheduled start time</LI>
1725   *   <LI>The task IDs of any tasks on which this task is dependent</LI>
1726   *   <LI>The action to take for this task if any of its dependencies fail</LI>
1727   *   <LI>The addresses of users to notify when this task completes</LI>
1728   *   <LI>The addresses of users to notify if this task fails</LI>
1729   * </UL>
1730   *
1731   * @return  A list of task properties that may be provided when scheduling any
1732   *          type of task.
1733   */
1734  public static List<TaskProperty> getCommonTaskProperties()
1735  {
1736    final List<TaskProperty> taskList = Arrays.asList(
1737         PROPERTY_TASK_ID,
1738         PROPERTY_SCHEDULED_START_TIME,
1739         PROPERTY_DEPENDENCY_ID,
1740         PROPERTY_FAILED_DEPENDENCY_ACTION,
1741         PROPERTY_NOTIFY_ON_COMPLETION,
1742         PROPERTY_NOTIFY_ON_ERROR);
1743
1744    return Collections.unmodifiableList(taskList);
1745  }
1746
1747
1748
1749  /**
1750   * Retrieves a list of task-specific properties that may be provided when
1751   * scheduling a task of this type.  This method should be overridden by
1752   * subclasses in order to provide an appropriate set of properties.
1753   *
1754   * @return  A list of task-specific properties that may be provided when
1755   *          scheduling a task of this type.
1756   */
1757  public List<TaskProperty> getTaskSpecificProperties()
1758  {
1759    return Collections.emptyList();
1760  }
1761
1762
1763
1764  /**
1765   * Retrieves the values of the task properties for this task.  The data type
1766   * of the values will vary based on the data type of the corresponding task
1767   * property and may be one of the following types:  {@code Boolean},
1768   * {@code Date}, {@code Long}, or {@code String}.  Task properties which do
1769   * not have any values will be included in the map with an empty value list.
1770   * <BR><BR>
1771   * Note that subclasses which have additional task properties should override
1772   * this method and return a map which contains both the property values from
1773   * this class (obtained from {@code super.getTaskPropertyValues()} and the
1774   * values of their own task-specific properties.
1775   *
1776   * @return  A map of the task property values for this task.
1777   */
1778  public Map<TaskProperty,List<Object>> getTaskPropertyValues()
1779  {
1780    final LinkedHashMap<TaskProperty,List<Object>> props =
1781         new LinkedHashMap<TaskProperty,List<Object>>();
1782
1783    props.put(PROPERTY_TASK_ID,
1784              Collections.<Object>unmodifiableList(Arrays.asList(taskID)));
1785
1786    if (scheduledStartTime == null)
1787    {
1788      props.put(PROPERTY_SCHEDULED_START_TIME, Collections.emptyList());
1789    }
1790    else
1791    {
1792      props.put(PROPERTY_SCHEDULED_START_TIME,
1793                Collections.<Object>unmodifiableList(Arrays.asList(
1794                     scheduledStartTime)));
1795    }
1796
1797    props.put(PROPERTY_DEPENDENCY_ID,
1798              Collections.<Object>unmodifiableList(dependencyIDs));
1799
1800    if (failedDependencyAction == null)
1801    {
1802      props.put(PROPERTY_FAILED_DEPENDENCY_ACTION, Collections.emptyList());
1803    }
1804    else
1805    {
1806      props.put(PROPERTY_FAILED_DEPENDENCY_ACTION,
1807                Collections.<Object>unmodifiableList(Arrays.asList(
1808                     failedDependencyAction.getName())));
1809    }
1810
1811    props.put(PROPERTY_NOTIFY_ON_COMPLETION,
1812              Collections.<Object>unmodifiableList(notifyOnCompletion));
1813
1814    props.put(PROPERTY_NOTIFY_ON_ERROR,
1815              Collections.<Object>unmodifiableList(notifyOnError));
1816
1817    return Collections.unmodifiableMap(props);
1818  }
1819
1820
1821
1822  /**
1823   * Retrieves a string representation of this task.
1824   *
1825   * @return  A string representation of this task.
1826   */
1827  @Override()
1828  public final String toString()
1829  {
1830    final StringBuilder buffer = new StringBuilder();
1831    toString(buffer);
1832    return buffer.toString();
1833  }
1834
1835
1836
1837  /**
1838   * Appends a string representation of this task to the provided buffer.
1839   *
1840   * @param  buffer  The buffer to which the string representation should be
1841   *                 provided.
1842   */
1843  public final void toString(final StringBuilder buffer)
1844  {
1845    buffer.append("Task(name='");
1846    buffer.append(getTaskName());
1847    buffer.append("', className='");
1848    buffer.append(taskClassName);
1849    buffer.append(", properties={");
1850
1851    boolean added = false;
1852    for (final Map.Entry<TaskProperty,List<Object>> e :
1853         getTaskPropertyValues().entrySet())
1854    {
1855      if (added)
1856      {
1857        buffer.append(", ");
1858      }
1859      else
1860      {
1861        added = true;
1862      }
1863
1864      buffer.append(e.getKey().getAttributeName());
1865      buffer.append("={");
1866
1867      final Iterator<Object> iterator = e.getValue().iterator();
1868      while (iterator.hasNext())
1869      {
1870        buffer.append('\'');
1871        buffer.append(String.valueOf(iterator.next()));
1872        buffer.append('\'');
1873
1874        if (iterator.hasNext())
1875        {
1876          buffer.append(',');
1877        }
1878      }
1879
1880      buffer.append('}');
1881    }
1882
1883    buffer.append("})");
1884  }
1885}