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