From 72c939b50428a2aa1f924a04ecdedee93e1383f4 Mon Sep 17 00:00:00 2001
From: kenneth_suter <kenneth_suter@localhost>
Date: Thu, 13 Sep 2007 21:14:20 +0000
Subject: [PATCH] Introduces a utility 'manage-tasks' for monitoring and some management of tasks called 'manage-tasks'. It can be used in either one-time or menu-driven execution mode to print a summary of task information, print verbose information about a particular task, or cancel a running or pending task.

---
 opends/src/server/org/opends/server/tools/tasks/TaskEntry.java                     |  482 ++++++++
 opends/src/server/org/opends/server/backends/task/TaskState.java                   |   40 
 opends/src/server/org/opends/server/util/cli/LDAPConnectionConsoleInteraction.java |   20 
 opends/src/server/org/opends/server/tools/tasks/TaskClientException.java           |   84 +
 opends/src/server/org/opends/server/tasks/InitializeTask.java                      |    7 
 opends/src/server/org/opends/server/util/args/LDAPConnectionArgumentParser.java    |  346 ++---
 opends/src/server/org/opends/server/backends/task/TaskScheduler.java               |   11 
 opends/src/server/org/opends/server/tools/ManageTasks.java                         |  926 ++++++++++++++++
 opends/src/server/org/opends/server/tasks/RebuildTask.java                         |    8 
 opends/src/server/org/opends/server/util/args/ArgumentParser.java                  |   24 
 opends/src/server/org/opends/server/tasks/DisconnectClientTask.java                |    7 
 opends/resource/bin/manage-tasks.bat                                               |   33 
 opends/src/server/org/opends/server/tasks/RestoreTask.java                         |   34 
 opends/src/server/org/opends/server/tasks/InitializeTargetTask.java                |    8 
 opends/src/server/org/opends/server/tasks/ImportTask.java                          |   90 +
 opends/src/server/org/opends/server/tasks/LeaveLockdownModeTask.java               |    8 
 opends/src/server/org/opends/server/util/StaticUtils.java                          |  195 +-
 opends/src/server/org/opends/server/tools/tasks/TaskScheduleInformation.java       |    2 
 opends/src/server/org/opends/server/backends/task/Task.java                        |   42 
 opends/src/server/org/opends/server/tasks/BackupTask.java                          |   62 +
 opends/src/messages/messages/tools.properties                                      |   66 +
 /dev/null                                                                          |  211 ---
 opends/src/server/org/opends/server/tasks/AddSchemaFileTask.java                   |    7 
 opends/src/server/org/opends/server/tasks/ExportTask.java                          |   74 +
 opends/src/server/org/opends/server/tasks/ShutdownTask.java                        |    6 
 opends/resource/bin/manage-tasks                                                   |   37 
 opends/src/server/org/opends/server/backends/task/FailedDependencyAction.java      |   23 
 opends/src/server/org/opends/server/tools/tasks/TaskTool.java                      |   34 
 opends/tests/unit-tests-testng/src/server/org/opends/server/tasks/DummyTask.java   |    7 
 opends/src/messages/messages/task.properties                                       |   70 +
 opends/src/server/org/opends/server/tools/tasks/TaskClient.java                    |  378 ++++++
 opends/src/server/org/opends/server/util/cli/ConsoleApplication.java               |   10 
 opends/src/server/org/opends/server/tasks/EnterLockdownModeTask.java               |    8 
 opends/src/server/org/opends/server/tasks/SetGenerationIdTask.java                 |    8 
 opends/src/server/org/opends/server/admin/client/cli/SecureConnectionCliArgs.java  |   14 
 35 files changed, 2,846 insertions(+), 536 deletions(-)

diff --git a/opends/resource/bin/manage-tasks b/opends/resource/bin/manage-tasks
new file mode 100644
index 0000000..dc53cf2
--- /dev/null
+++ b/opends/resource/bin/manage-tasks
@@ -0,0 +1,37 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License").  You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at
+# trunk/opends/resource/legal-notices/OpenDS.LICENSE
+# or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at
+# trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+# add the following below this CDDL HEADER, with the fields enclosed
+# by brackets "[]" replaced with your own identifying information:
+#      Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#      Portions Copyright 2006-2007 Sun Microsystems, Inc.
+
+
+# This script may be used to manage tasks in the Directory Server.
+OPENDS_INVOKE_CLASS="org.opends.server.tools.ManageTasks"
+export OPENDS_INVOKE_CLASS
+
+SCRIPT_NAME_ARG="-Dorg.opends.server.scriptName=manage-tasks"
+export SCRIPT_NAME_ARG
+
+SCRIPT_DIR=`dirname "${0}"`
+"${SCRIPT_DIR}/../lib/_client-script.sh" "${@}"
diff --git a/opends/resource/bin/manage-tasks.bat b/opends/resource/bin/manage-tasks.bat
new file mode 100644
index 0000000..7edaaf1
--- /dev/null
+++ b/opends/resource/bin/manage-tasks.bat
@@ -0,0 +1,33 @@
+
+@echo off
+rem CDDL HEADER START
+rem
+rem The contents of this file are subject to the terms of the
+rem Common Development and Distribution License, Version 1.0 only
+rem (the "License").  You may not use this file except in compliance
+rem with the License.
+rem
+rem You can obtain a copy of the license at
+rem trunk/opends/resource/legal-notices/OpenDS.LICENSE
+rem or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+rem See the License for the specific language governing permissions
+rem and limitations under the License.
+rem
+rem When distributing Covered Code, include this CDDL HEADER in each
+rem file and include the License file at
+rem trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+rem add the following below this CDDL HEADER, with the fields enclosed
+rem by brackets "[]" replaced with your own identifying information:
+rem      Portions Copyright [yyyy] [name of copyright owner]
+rem
+rem CDDL HEADER END
+rem
+rem
+rem      Portions Copyright 2006-2007 Sun Microsystems, Inc.
+
+setlocal
+
+set OPENDS_INVOKE_CLASS="org.opends.server.tools.ManageTasks"
+set SCRIPT_NAME_ARG="-Dorg.opends.server.scriptName=manage-tasks"
+for %%i in (%~sf0) do call "%%~dPsi\..\lib\_client-script.bat" %*
+
diff --git a/opends/src/messages/messages/task.properties b/opends/src/messages/messages/task.properties
index dcbcaea..a751b3a 100644
--- a/opends/src/messages/messages/task.properties
+++ b/opends/src/messages/messages/task.properties
@@ -116,3 +116,73 @@
  connection with connection ID %s
 INFO_TASK_DISCONNECT_MESSAGE_31=An administrator has terminated this client \
  connection
+INFO_TASK_ADD_SCHEMA_FILE_NAME_32=Add Schema File
+INFO_TASK_BACKUP_NAME_33=Backup
+INFO_TASK_DISCONNECT_CLIENT_NAME_34=Disconnect Client
+INFO_TASK_ENTER_LOCKDOWN_MODE_NAME_35=Lockdown
+INFO_TASK_EXPORT_NAME_36=Export
+INFO_TASK_IMPORT_NAME_37=Import
+INFO_TASK_INITIALIZE_TARGET_NAME_38=Initialize Backend
+INFO_TASK_INITIALIZE_NAME_39=Initialize From Replica
+INFO_TASK_LEAVE_LOCKDOWN_MODE_NAME_40=Leave Lockdown
+INFO_TASK_REBUILD_NAME_41=Rebuild Index
+INFO_TASK_RESTORE_NAME_42=Restore
+INFO_TASK_SET_GENERATION_ID_NAME_43=Set Generation ID
+INFO_TASK_SHUTDOWN_NAME_44=Shutdown
+INFO_TASK_STATE_UNSCHEDULED_45=Unscheduled
+INFO_TASK_STATE_DISABLED_46=Disabled
+INFO_TASK_STATE_WAITING_ON_START_TIME_47=Waiting on start time
+INFO_TASK_STATE_WAITING_ON_DEPENDENCY_48=Waiting on dependency
+INFO_TASK_STATE_RUNNING_49=Running
+INFO_TASK_STATE_COMPLETED_SUCCESSFULLY_50=Completed successfully
+INFO_TASK_STATE_COMPLETED_WITH_ERRORS_51=Completed with errors
+INFO_TASK_STATE_STOPPED_BY_SHUTDOWN_52=Stopped by shutdown
+INFO_TASK_STATE_STOPPED_BY_ERROR_53=Stopped by error
+INFO_TASK_STATE_STOPPED_BY_ADMINISTRATOR_54=Stopped by administrator
+INFO_TASK_STATE_CANCELED_BEFORE_STARTING_55=Canceled before starting
+INFO_BACKUP_ARG_BACKUPALL_56=Backup All
+INFO_BACKUP_ARG_COMPRESS_57=Compress
+INFO_BACKUP_ARG_ENCRYPT_58=Encrypt
+INFO_BACKUP_ARG_HASH_59=Hash
+INFO_BACKUP_ARG_INCREMENTAL_60=Incremental
+INFO_BACKUP_ARG_SIGN_HASH_61=Sign Hash
+INFO_BACKUP_ARG_BACKEND_IDS_62=Backend ID(s)
+INFO_BACKUP_ARG_BACKUP_DIR_63=Backup Directory
+INFO_BACKUP_ARG_BACKUP_ID_64=Backup ID
+INFO_BACKUP_ARG_INC_BASE_ID_65=Incremental Base ID
+INFO_EXPORT_ARG_LDIF_FILE_66=LDIF File
+INFO_EXPORT_ARG_BACKEND_ID_67=Backend ID
+INFO_EXPORT_ARG_APPEND_TO_LDIF_68=Append To LDIF
+INFO_EXPORT_ARG_COMPRESS_LDIF_69=Comress LDIF
+INFO_EXPORT_ARG_ENCRYPT_LDIF_70=Encrypt LDIF
+INFO_EXPORT_ARG_SIGN_HASH_71=Sign Hash
+INFO_EXPORT_ARG_INCL_ATTR_72=Include Attribute
+INFO_EXPORT_ARG_EXCL_ATTR_73=Exclude Attribute
+INFO_EXPORT_ARG_INCL_FILTER_74=Include Filter
+INFO_EXPORT_ARG_EXCL_FILTER_75=Exclude Filter
+INFO_EXPORT_ARG_INCL_BRANCH_76=Include Branch
+INFO_EXPORT_ARG_EXCL_BRANCH_77=Exclude Branch
+INFO_EXPORT_ARG_WRAP_COLUMN_78=Wrap Column
+INFO_RESTORE_ARG_BACKUP_DIR_79=Backup Directory
+INFO_RESTORE_ARG_BACKUP_ID_80=Backup ID
+INFO_RESTORE_ARG_VERIFY_ONLY_81=Verify Only
+INFO_IMPORT_ARG_LDIF_FILE_82=LDIF File
+INFO_IMPORT_ARG_APPEND_83=Append
+INFO_IMPORT_ARG_REPLACE_EXISTING_84=Replace Existing
+INFO_IMPORT_ARG_BACKEND_ID_85=Backend ID
+INFO_IMPORT_ARG_INCL_ATTR_86=Include Attribute
+INFO_IMPORT_ARG_EXCL_ATTR_87=Exclude Attribute
+INFO_IMPORT_ARG_INCL_FILTER_88=Include Filter
+INFO_IMPORT_ARG_EXCL_FILTER_89=Exclude Filter
+INFO_IMPORT_ARG_INCL_BRANCH_90=Include Branch
+INFO_IMPORT_ARG_EXCL_BRANCH_91=Exclude Branch
+INFO_IMPORT_ARG_REJECT_FILE_92=Reject File
+INFO_IMPORT_ARG_SKIP_FILE_93=Skip File
+INFO_IMPORT_ARG_OVERWRITE_94=Overwrite
+INFO_IMPORT_ARG_SKIP_SCHEMA_VALIDATION_95=Skip Schema Validation
+INFO_IMPORT_ARG_IS_COMPRESSED_96=Is Compressed
+INFO_IMPORT_ARG_IS_ENCRYPTED_97=Is Encrypted
+INFO_IMPORT_ARG_CLEAR_BACKEND_98=Clear Backend
+INFO_FAILED_DEPENDENCY_ACTION_PROCESS_99=Process
+INFO_FAILED_DEPENDENCY_ACTION_CANCEL_100=Cancel
+INFO_FAILED_DEPENDENCY_ACTION_DISABLE_101=Disable
\ No newline at end of file
diff --git a/opends/src/messages/messages/tools.properties b/opends/src/messages/messages/tools.properties
index fc8c231..7b8efad 100644
--- a/opends/src/messages/messages/tools.properties
+++ b/opends/src/messages/messages/tools.properties
@@ -1895,13 +1895,13 @@
  connection to the Directory Server was closed while waiting for a response to \
  the shutdown request.  This likely means that the server has started the \
  shutdown process
-SEVERE_ERR_TASK_CLIENT_IO_ERROR_1316=ERROR:  An I/O error occurred while \
+SEVERE_ERR_TASK_TOOL_IO_ERROR_1316=ERROR:  An I/O error occurred while \
  attempting to communicate with the Directory Server:  %s
-SEVERE_ERR_TASK_CLIENT_DECODE_ERROR_1317=ERROR:  An error occurred while \
+SEVERE_ERR_TASK_TOOL_DECODE_ERROR_1317=ERROR:  An error occurred while \
  trying to decode the response from the server:  %s
 SEVERE_ERR_TASK_CLIENT_INVALID_RESPONSE_TYPE_1318=ERROR:  Expected an add \
  response message but got a %s message instead
-INFO_TASK_CLIENT_TASK_SCHEDULED_1319=Scheduled task %s
+INFO_TASK_TOOL_TASK_SCHEDULED_1319=Scheduled task %s
 SEVERE_ERR_LDAP_CONN_INCOMPATIBLE_ARGS_1320=ERROR:  argument %s is \
  incompatible with use of this tool to interact with the directory as a client
 SEVERE_ERR_CREATERC_ONLY_RUNS_ON_UNIX_1321=This tool may only be used on \
@@ -2080,4 +2080,62 @@
  or Start TLS (using option %s)
 SEVERE_ERR_UPGRADE_INCOMPATIBLE_ARGS_1411=The argument '%s' is incompatible \
  with '%s'
-
+INFO_TASKINFO_TOOL_DESCRIPTION_1412=This utility can be used to obtain a list \
+ of tasks scheduled to run within the Directory Server as well as information \
+ about individual tasks
+INFO_TASKINFO_SUMMARY_ARG_DESCRIPTION_1413=Print a summary of tasks
+INFO_TASKINFO_TASK_ARG_DESCRIPTION_1414=Specifies the ID of a particular task \
+ about which this tool will display information
+INFO_TASKINFO_CMD_REFRESH_1415=refresh
+INFO_TASKINFO_CMD_CANCEL_1416=cancel task
+INFO_TASKINFO_CMD_VIEW_LOGS_1417=view logs
+INFO_TASKINFO_MENU_PROMPT_1418=Enter a menu item or task number
+INFO_TASKINFO_CMD_CANCEL_NUMBER_PROMPT_1419=Enter the number of a task to \
+  cancel [%d]
+INFO_TASKINFO_MENU_1420=Menu
+MILD_ERR_TASKINFO_INVALID_TASK_NUMBER_1421=Task number must be between 1 and \
+  %d
+MILD_ERR_TASKINFO_INVALID_MENU_KEY_1422=Invalid menu item or task number '%s'
+INFO_TASKINFO_FIELD_ID_1423=ID
+INFO_TASKINFO_FIELD_TYPE_1424=Type
+INFO_TASKINFO_FIELD_STATUS_1425=Status
+INFO_TASKINFO_FIELD_SCHEDULED_START_1426=Scheduled Start Time
+INFO_TASKINFO_FIELD_ACTUAL_START_1427=Actual Start Time
+INFO_TASKINFO_FIELD_COMPLETION_TIME_1428=Completion Time
+INFO_TASKINFO_FIELD_DEPENDENCY_1429=Dependencies
+INFO_TASKINFO_FIELD_FAILED_DEPENDENCY_ACTION_1430=Failed Dependency Action
+INFO_TASKINFO_FIELD_LOG_1431=Log Message(s)
+INFO_TASKINFO_FIELD_LAST_LOG_1432=Last Log Message
+INFO_TASKINFO_FIELD_NOTIFY_ON_COMPLETION_1433=Email Upon Completion
+INFO_TASKINFO_FIELD_NOTIFY_ON_ERROR_1434=Email Upon Error
+INFO_TASKINFO_CMD_CANCEL_SUCCESS_1435=Task %s canceled
+SEVERE_ERR_TASKINFO_CMD_CANCEL_ERROR_1436=Error canceling task %s:  %s
+SEVERE_ERR_TASKINFO_RETRIEVING_TASK_ENTRY_1437=Error retrieving task entry \
+  %s:  %s
+MILD_ERR_TASKINFO_UNKNOWN_TASK_ENTRY_1438=There are no tasks with ID %s
+INFO_TASKINFO_DETAILS_1439=Task Details
+INFO_TASKINFO_OPTIONS_1440=%s Options
+INFO_TASKINFO_NO_TASKS_1441=No tasks exist
+INFO_TASKINFO_NONE_1442=None
+INFO_TASKINFO_NONE_SPECIFIED_1443=None Specified
+INFO_TASKINFO_IMMEDIATE_EXECUTION_1444=Immediate execution
+INFO_TASKINFO_LDAP_EXCEPTION_1445=Error connecting to the directory server: \
+  '%s'. Verify that the connection options are correct and that the server is \
+  running
+SEVERE_ERR_INCOMPATIBLE_ARGUMENTS_1446=Options '%s' and '%s' are incompatible \
+  with each other and cannot be used together
+INFO_TASKINFO_TASK_ARG_CANCEL_1447=Specifies the ID of a particular task \
+  to cancel
+SEVERE_ERR_TASKINFO_CANCELING_TASK_1448=Error canceling task '%s': %s
+SEVERE_ERR_TASKINFO_ACCESSING_LOGS_1449=Error accessing logs for task '%s': %s
+SEVERE_ERR_TASKINFO_NOT_CANCELABLE_TASK_INDEX_1450=Task at index %d is not \
+  cancelable
+SEVERE_ERR_TASKINFO_NOT_CANCELABLE_TASK_1451=Task %s has finished and cannot \
+  be canceled
+INFO_TASKINFO_NO_CANCELABLE_TASKS_1452=There are currently no cancelable tasks
+SEVERE_ERR_TASK_CLIENT_UNKNOWN_TASK_1453=There are no tasks defined with ID '%s'
+SEVERE_ERR_TASK_CLIENT_UNCANCELABLE_TASK_1454=Task '%s' is has finished and \
+  cannot be canceled
+SEVERE_ERR_TASK_CLIENT_TASK_STATE_UNKNOWN_1455=State for task '%s' cannot be \
+  determined
+SEVERE_ERR_XXX_1455=xxx
\ No newline at end of file
diff --git a/opends/src/server/org/opends/server/admin/client/cli/SecureConnectionCliArgs.java b/opends/src/server/org/opends/server/admin/client/cli/SecureConnectionCliArgs.java
index 6215e12..fe1dcb5 100644
--- a/opends/src/server/org/opends/server/admin/client/cli/SecureConnectionCliArgs.java
+++ b/opends/src/server/org/opends/server/admin/client/cli/SecureConnectionCliArgs.java
@@ -149,6 +149,12 @@
    */
   public BooleanArgument useStartTLSArg = null;
 
+  /**
+   * Argument indicating a SASL option.
+   */
+  public StringArgument  saslOptionArg = null;
+
+
   // the trust manager.
   private ApplicationTrustManager trustManager;
 
@@ -391,6 +397,14 @@
         INFO_DESCRIPTION_BINDPASSWORDFILE.get());
     set.add(bindPasswordFileArg);
 
+    saslOptionArg = new StringArgument(
+            "sasloption", OPTION_SHORT_SASLOPTION,
+            OPTION_LONG_SASLOPTION, false,
+            true, true,
+            OPTION_VALUE_SASLOPTION, null, null,
+            INFO_LDAP_CONN_DESCRIPTION_SASLOPTIONS.get());
+    set.add(saslOptionArg);
+
     trustAllArg = new BooleanArgument("trustAll", OPTION_SHORT_TRUSTALL,
         OPTION_LONG_TRUSTALL, INFO_DESCRIPTION_TRUSTALL.get());
     set.add(trustAllArg);
diff --git a/opends/src/server/org/opends/server/backends/task/FailedDependencyAction.java b/opends/src/server/org/opends/server/backends/task/FailedDependencyAction.java
index aea4c64..68b38d3 100644
--- a/opends/src/server/org/opends/server/backends/task/FailedDependencyAction.java
+++ b/opends/src/server/org/opends/server/backends/task/FailedDependencyAction.java
@@ -26,6 +26,8 @@
  */
 package org.opends.server.backends.task;
 
+import org.opends.messages.Message;
+import org.opends.messages.TaskMessages;
 
 
 /**
@@ -39,14 +41,14 @@
    * The action that indicates that the dependent task should be processed
    * anyway.
    */
-  PROCESS,
+  PROCESS(TaskMessages.INFO_FAILED_DEPENDENCY_ACTION_PROCESS.get()),
 
 
 
   /**
    * The action that indicates that the dependent task should be canceled.
    */
-  CANCEL,
+  CANCEL(TaskMessages.INFO_FAILED_DEPENDENCY_ACTION_CANCEL.get()),
 
 
 
@@ -54,7 +56,7 @@
    * The action that indicates that the dependent task should be disabled so
    * that an administrator will have to re-enable it before it can start.
    */
-  DISABLE;
+  DISABLE(TaskMessages.INFO_FAILED_DEPENDENCY_ACTION_DISABLE.get());
 
 
 
@@ -88,5 +90,20 @@
       return null;
     }
   }
+
+  private Message name;
+
+  /**
+   * Gets the display name of this action.
+   *
+   * @return Message representing the name of this action
+   */
+  public Message getDisplayName() {
+    return name;
+  }
+
+  private FailedDependencyAction(Message name) {
+    this.name = name;
+  }
 }
 
diff --git a/opends/src/server/org/opends/server/backends/task/Task.java b/opends/src/server/org/opends/server/backends/task/Task.java
index 1a83f2d..fde6022 100644
--- a/opends/src/server/org/opends/server/backends/task/Task.java
+++ b/opends/src/server/org/opends/server/backends/task/Task.java
@@ -144,7 +144,34 @@
   // The scheduler with which this task is associated.
   private TaskScheduler taskScheduler;
 
+  /**
+   * Gets a message that identifies this type of task suitable for
+   * presentation to humans in monitoring tools.
+   *
+   * @return name of task
+   */
+  public Message getDisplayName() {
+    // NOTE: this method is invoked via reflection.  If you rename
+    // it be sure to modify the calls.
+    return null;
+  };
 
+  /**
+   * Given an attribute type name returns and locale sensitive
+   * representation.
+   *
+   * @param name of an attribute type associated with the object
+   *        class that represents this entry in the directory
+   * @return Message diaplay name
+   */
+  public Message getAttributeDisplayName(String name) {
+    // Subclasses that are schedulable from the task interface
+    // should override this
+
+    // NOTE: this method is invoked via reflection.  If you rename
+    // it be sure to modify the calls.
+    return null;
+  }
 
   /**
    * Performs generic initialization for this task based on the information in
@@ -1224,6 +1251,21 @@
   public void interruptTask(TaskState interruptState, Message interruptReason)
   {
     // No action is performed by default.
+
+    // NOTE:  if you implement this make sure to override isInterruptable
+    //        to return 'true'
   }
+
+
+
+  /**
+   * Indicates whether or not this task is interruptable or not.
+   *
+   * @return boolean where true indicates that this task can be interrupted.
+   */
+  public boolean isInterruptable() {
+    return false;
+  }
+
 }
 
diff --git a/opends/src/server/org/opends/server/backends/task/TaskScheduler.java b/opends/src/server/org/opends/server/backends/task/TaskScheduler.java
index 285c8da..b835337 100644
--- a/opends/src/server/org/opends/server/backends/task/TaskScheduler.java
+++ b/opends/src/server/org/opends/server/backends/task/TaskScheduler.java
@@ -347,6 +347,10 @@
         activeThreads.put(task.getTaskID(), taskThread);
         taskThread.setTask(task);
       }
+      else if (TaskState.isDone(state))
+      {
+        completedTasks.add(task);
+      }
       else
       {
         pendingTasks.add(task);
@@ -871,6 +875,13 @@
    */
   private TaskState shouldStart(Task task)
   {
+    // If the task has finished we don't want to restart it
+    TaskState state = task.getTaskState();
+    if (state != null && TaskState.isDone(state))
+    {
+      return state;
+    }
+
     if (! isRunning)
     {
       return TaskState.UNSCHEDULED;
diff --git a/opends/src/server/org/opends/server/backends/task/TaskState.java b/opends/src/server/org/opends/server/backends/task/TaskState.java
index e3f86b3..c0b36fc 100644
--- a/opends/src/server/org/opends/server/backends/task/TaskState.java
+++ b/opends/src/server/org/opends/server/backends/task/TaskState.java
@@ -26,6 +26,9 @@
  */
 package org.opends.server.backends.task;
 
+import org.opends.messages.Message;
+import static org.opends.messages.TaskMessages.*;
+
 
 /**
  * This enumeration defines the various states that a task can have during its
@@ -37,7 +40,7 @@
    * The task state that indicates that the task has not yet been scheduled,
    * or possibly that the scheduler is currently not running.
    */
-  UNSCHEDULED,
+  UNSCHEDULED(INFO_TASK_STATE_UNSCHEDULED.get()),
 
 
 
@@ -45,7 +48,7 @@
    * The task state that indicates that the task has been disabled by an
    * administrator.
    */
-  DISABLED,
+  DISABLED(INFO_TASK_STATE_DISABLED.get()),
 
 
 
@@ -53,7 +56,7 @@
    * The task state that indicates that the task's scheduled start time has not
    * yet arrived.
    */
-  WAITING_ON_START_TIME,
+  WAITING_ON_START_TIME(INFO_TASK_STATE_WAITING_ON_START_TIME.get()),
 
 
 
@@ -61,14 +64,14 @@
    * The task state that indicates that at least one of the task's defined
    * dependencies has not yet completed.
    */
-  WAITING_ON_DEPENDENCY,
+  WAITING_ON_DEPENDENCY(INFO_TASK_STATE_WAITING_ON_DEPENDENCY.get()),
 
 
 
   /**
    * The task state that indicates that the task is currently running.
    */
-  RUNNING,
+  RUNNING(INFO_TASK_STATE_RUNNING.get()),
 
 
 
@@ -76,7 +79,7 @@
    * The task state that indicates that the task has completed without any
    * errors.
    */
-  COMPLETED_SUCCESSFULLY,
+  COMPLETED_SUCCESSFULLY(INFO_TASK_STATE_COMPLETED_SUCCESSFULLY.get()),
 
 
 
@@ -85,7 +88,7 @@
    * intended goal, but that one or more errors were encountered during the
    * process.
    */
-  COMPLETED_WITH_ERRORS,
+  COMPLETED_WITH_ERRORS(INFO_TASK_STATE_COMPLETED_WITH_ERRORS.get()),
 
 
 
@@ -93,7 +96,7 @@
    * The task state that indicates that the task was unable to complete because
    * it was interrupted by the shutdown of the task backend.
    */
-  STOPPED_BY_SHUTDOWN,
+  STOPPED_BY_SHUTDOWN(INFO_TASK_STATE_STOPPED_BY_SHUTDOWN.get()),
 
 
 
@@ -101,7 +104,7 @@
    * The task state that indicates that one or more errors prevented the task
    * from completing.
    */
-  STOPPED_BY_ERROR,
+  STOPPED_BY_ERROR(INFO_TASK_STATE_STOPPED_BY_ERROR.get()),
 
 
 
@@ -109,7 +112,7 @@
    * The task state that indicates that the task was stopped by an administrator
    * after it had already started but before it was able to complete.
    */
-  STOPPED_BY_ADMINISTRATOR,
+  STOPPED_BY_ADMINISTRATOR(INFO_TASK_STATE_STOPPED_BY_ADMINISTRATOR.get()),
 
 
 
@@ -117,7 +120,7 @@
    * The task state that indicates that the task was canceled by an
    * administrator before it started running.
    */
-  CANCELED_BEFORE_STARTING;
+  CANCELED_BEFORE_STARTING(INFO_TASK_STATE_CANCELED_BEFORE_STARTING.get());
 
 
 
@@ -282,5 +285,20 @@
       return null;
     }
   }
+
+  private Message displayName;
+
+  /**
+   * Gets a locale sensitive representation of this state.
+   *
+   * @return Message describing state
+   */
+  public Message getDisplayName() {
+    return displayName;
+  }
+
+  private TaskState(Message displayName) {
+    this.displayName = displayName;
+  }
 }
 
diff --git a/opends/src/server/org/opends/server/tasks/AddSchemaFileTask.java b/opends/src/server/org/opends/server/tasks/AddSchemaFileTask.java
index 148e322..d44bd61 100644
--- a/opends/src/server/org/opends/server/tasks/AddSchemaFileTask.java
+++ b/opends/src/server/org/opends/server/tasks/AddSchemaFileTask.java
@@ -83,7 +83,12 @@
   // The list of files to be added to the server schema.
   TreeSet<String> filesToAdd;
 
-
+  /**
+   * {@inheritDoc}
+   */
+  public Message getDisplayName() {
+    return INFO_TASK_ADD_SCHEMA_FILE_NAME.get();
+  }
 
   /**
    * {@inheritDoc}
diff --git a/opends/src/server/org/opends/server/tasks/BackupTask.java b/opends/src/server/org/opends/server/tasks/BackupTask.java
index f04ec5d..f9aba64 100644
--- a/opends/src/server/org/opends/server/tasks/BackupTask.java
+++ b/opends/src/server/org/opends/server/tasks/BackupTask.java
@@ -60,6 +60,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.TimeZone;
+import java.util.HashMap;
 import java.text.SimpleDateFormat;
 import java.io.File;
 
@@ -72,6 +73,53 @@
 {
 
 
+  /**
+   * Stores mapping between configuration attribute name and its label.
+   */
+  static private Map<String,Message> argDisplayMap =
+          new HashMap<String,Message>();
+  static {
+    argDisplayMap.put(
+            ATTR_TASK_BACKUP_ALL,
+            INFO_BACKUP_ARG_BACKUPALL.get());
+
+    argDisplayMap.put(
+            ATTR_TASK_BACKUP_COMPRESS,
+            INFO_BACKUP_ARG_COMPRESS.get());
+
+    argDisplayMap.put(
+            ATTR_TASK_BACKUP_ENCRYPT,
+            INFO_BACKUP_ARG_ENCRYPT.get());
+
+    argDisplayMap.put(
+            ATTR_TASK_BACKUP_HASH,
+            INFO_BACKUP_ARG_HASH.get());
+
+    argDisplayMap.put(
+            ATTR_TASK_BACKUP_INCREMENTAL,
+            INFO_BACKUP_ARG_INCREMENTAL.get());
+
+    argDisplayMap.put(
+            ATTR_TASK_BACKUP_SIGN_HASH,
+            INFO_BACKUP_ARG_SIGN_HASH.get());
+
+    argDisplayMap.put(
+            ATTR_TASK_BACKUP_BACKEND_ID,
+            INFO_BACKUP_ARG_BACKEND_IDS.get());
+
+    argDisplayMap.put(
+            ATTR_BACKUP_ID,
+            INFO_BACKUP_ARG_BACKUP_ID.get());
+
+    argDisplayMap.put(
+            ATTR_BACKUP_DIRECTORY_PATH,
+            INFO_BACKUP_ARG_BACKUP_DIR.get());
+
+    argDisplayMap.put(
+            ATTR_TASK_BACKUP_INCREMENTAL_BASE_ID,
+            INFO_BACKUP_ARG_INC_BASE_ID.get());
+  }
+
 
   // The task arguments.
   private boolean backUpAll;
@@ -96,6 +144,20 @@
   /**
    * {@inheritDoc}
    */
+  public Message getDisplayName() {
+    return INFO_TASK_BACKUP_NAME.get();
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public Message getAttributeDisplayName(String attrName) {
+    return argDisplayMap.get(attrName);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
   @Override public void initializeTask() throws DirectoryException
   {
     // If the client connection is available, then make sure the associated
diff --git a/opends/src/server/org/opends/server/tasks/DisconnectClientTask.java b/opends/src/server/org/opends/server/tasks/DisconnectClientTask.java
index 9298e61..f8ea940 100644
--- a/opends/src/server/org/opends/server/tasks/DisconnectClientTask.java
+++ b/opends/src/server/org/opends/server/tasks/DisconnectClientTask.java
@@ -69,7 +69,12 @@
   // The disconnect message to send to the client.
   private Message disconnectMessage;
 
-
+  /**
+   * {@inheritDoc}
+   */
+  public Message getDisplayName() {
+    return INFO_TASK_DISCONNECT_CLIENT_NAME.get();
+  }
 
   /**
    * {@inheritDoc}
diff --git a/opends/src/server/org/opends/server/tasks/EnterLockdownModeTask.java b/opends/src/server/org/opends/server/tasks/EnterLockdownModeTask.java
index 15a8a9c..270c742 100644
--- a/opends/src/server/org/opends/server/tasks/EnterLockdownModeTask.java
+++ b/opends/src/server/org/opends/server/tasks/EnterLockdownModeTask.java
@@ -50,6 +50,14 @@
 public class EnterLockdownModeTask
        extends Task
 {
+
+  /**
+   * {@inheritDoc}
+   */
+  public Message getDisplayName() {
+    return INFO_TASK_ENTER_LOCKDOWN_MODE_NAME.get();
+  }
+
   /**
    * {@inheritDoc}
    */
diff --git a/opends/src/server/org/opends/server/tasks/ExportTask.java b/opends/src/server/org/opends/server/tasks/ExportTask.java
index 8ac3217..10432f5 100644
--- a/opends/src/server/org/opends/server/tasks/ExportTask.java
+++ b/opends/src/server/org/opends/server/tasks/ExportTask.java
@@ -53,6 +53,8 @@
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
 
 /**
  * This class provides an implementation of a Directory Server task that can
@@ -61,7 +63,64 @@
 public class ExportTask extends Task
 {
 
+  /**
+   * Stores mapping between configuration attribute name and its label.
+   */
+  static private Map<String,Message> argDisplayMap =
+          new HashMap<String,Message>();
+  static {
+    argDisplayMap.put(
+            ATTR_TASK_EXPORT_LDIF_FILE,
+            INFO_EXPORT_ARG_LDIF_FILE.get());
 
+    argDisplayMap.put(
+            ATTR_TASK_EXPORT_BACKEND_ID,
+            INFO_EXPORT_ARG_BACKEND_ID.get());
+
+    argDisplayMap.put(
+            ATTR_TASK_EXPORT_APPEND_TO_LDIF,
+            INFO_EXPORT_ARG_APPEND_TO_LDIF.get());
+
+    argDisplayMap.put(
+            ATTR_TASK_EXPORT_COMPRESS_LDIF,
+            INFO_EXPORT_ARG_COMPRESS_LDIF.get());
+
+    argDisplayMap.put(
+            ATTR_TASK_EXPORT_ENCRYPT_LDIF,
+            INFO_EXPORT_ARG_ENCRYPT_LDIF.get());
+
+    argDisplayMap.put(
+            ATTR_TASK_EXPORT_SIGN_HASH,
+            INFO_EXPORT_ARG_SIGN_HASH.get());
+
+    argDisplayMap.put(
+            ATTR_TASK_EXPORT_INCLUDE_ATTRIBUTE,
+            INFO_EXPORT_ARG_INCL_ATTR.get());
+
+    argDisplayMap.put(
+            ATTR_TASK_EXPORT_EXCLUDE_ATTRIBUTE,
+            INFO_EXPORT_ARG_EXCL_ATTR.get());
+
+    argDisplayMap.put(
+            ATTR_TASK_EXPORT_INCLUDE_FILTER,
+            INFO_EXPORT_ARG_INCL_FILTER.get());
+
+    argDisplayMap.put(
+            ATTR_TASK_EXPORT_EXCLUDE_FILTER,
+            INFO_EXPORT_ARG_EXCL_FILTER.get());
+
+    argDisplayMap.put(
+            ATTR_TASK_EXPORT_INCLUDE_BRANCH,
+            INFO_EXPORT_ARG_INCL_BRANCH.get());
+
+    argDisplayMap.put(
+            ATTR_TASK_EXPORT_EXCLUDE_BRANCH,
+            INFO_EXPORT_ARG_EXCL_BRANCH.get());
+
+    argDisplayMap.put(
+            ATTR_TASK_EXPORT_WRAP_COLUMN,
+            INFO_EXPORT_ARG_WRAP_COLUMN.get());
+  }
 
   private String  ldifFile;
   private String  backendID;
@@ -77,6 +136,21 @@
   private ArrayList<String> includeBranchStrings;
   private ArrayList<String> excludeBranchStrings;
 
+
+  /**
+   * {@inheritDoc}
+   */
+  public Message getDisplayName() {
+    return INFO_TASK_EXPORT_NAME.get();
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public Message getAttributeDisplayName(String name) {
+    return argDisplayMap.get(name);
+  }
+
   /**
    * {@inheritDoc}
    */
diff --git a/opends/src/server/org/opends/server/tasks/ImportTask.java b/opends/src/server/org/opends/server/tasks/ImportTask.java
index 3027835..2d451dc 100644
--- a/opends/src/server/org/opends/server/tasks/ImportTask.java
+++ b/opends/src/server/org/opends/server/tasks/ImportTask.java
@@ -59,6 +59,8 @@
 import java.util.HashSet;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
 
 /**
  * This class provides an implementation of a Directory Server task that can
@@ -72,6 +74,81 @@
   private static final DebugTracer TRACER = getTracer();
 
 
+  /**
+   * Stores mapping between configuration attribute name and its label.
+   */
+  static private Map<String,Message> argDisplayMap =
+          new HashMap<String,Message>();
+
+  static {
+    argDisplayMap.put(
+            ATTR_IMPORT_LDIF_FILE,
+            INFO_IMPORT_ARG_LDIF_FILE.get());
+
+    argDisplayMap.put(
+            ATTR_IMPORT_APPEND,
+            INFO_IMPORT_ARG_APPEND.get());
+
+    argDisplayMap.put(
+            ATTR_IMPORT_REPLACE_EXISTING,
+            INFO_IMPORT_ARG_REPLACE_EXISTING.get());
+
+    argDisplayMap.put(
+            ATTR_IMPORT_BACKEND_ID,
+            INFO_IMPORT_ARG_BACKEND_ID.get());
+
+    argDisplayMap.put(
+            ATTR_IMPORT_INCLUDE_BRANCH,
+            INFO_IMPORT_ARG_INCL_BRANCH.get());
+
+    argDisplayMap.put(
+            ATTR_IMPORT_EXCLUDE_BRANCH,
+            INFO_IMPORT_ARG_EXCL_BRANCH.get());
+
+    argDisplayMap.put(
+            ATTR_IMPORT_INCLUDE_ATTRIBUTE,
+            INFO_IMPORT_ARG_INCL_ATTR.get());
+
+    argDisplayMap.put(
+            ATTR_IMPORT_EXCLUDE_ATTRIBUTE,
+            INFO_IMPORT_ARG_EXCL_ATTR.get());
+
+    argDisplayMap.put(
+            ATTR_IMPORT_INCLUDE_FILTER,
+            INFO_IMPORT_ARG_INCL_FILTER.get());
+
+    argDisplayMap.put(
+            ATTR_IMPORT_EXCLUDE_FILTER,
+            INFO_IMPORT_ARG_EXCL_FILTER.get());
+
+    argDisplayMap.put(
+            ATTR_IMPORT_REJECT_FILE,
+            INFO_IMPORT_ARG_REJECT_FILE.get());
+
+    argDisplayMap.put(
+            ATTR_IMPORT_SKIP_FILE,
+            INFO_IMPORT_ARG_SKIP_FILE.get());
+
+    argDisplayMap.put(
+            ATTR_IMPORT_OVERWRITE,
+            INFO_IMPORT_ARG_OVERWRITE.get());
+
+    argDisplayMap.put(
+            ATTR_IMPORT_SKIP_SCHEMA_VALIDATION,
+            INFO_IMPORT_ARG_SKIP_SCHEMA_VALIDATION.get());
+
+    argDisplayMap.put(
+            ATTR_IMPORT_IS_COMPRESSED,
+            INFO_IMPORT_ARG_IS_COMPRESSED.get());
+
+    argDisplayMap.put(
+            ATTR_IMPORT_IS_ENCRYPTED,
+            INFO_IMPORT_ARG_IS_ENCRYPTED.get());
+
+    argDisplayMap.put(
+            ATTR_IMPORT_CLEAR_BACKEND,
+            INFO_IMPORT_ARG_CLEAR_BACKEND.get());
+  }
 
 
   boolean append                  = false;
@@ -93,6 +170,19 @@
   ArrayList<String>  ldifFiles               = null;
 
 
+  /**
+   * {@inheritDoc}
+   */
+  public Message getDisplayName() {
+    return INFO_TASK_IMPORT_NAME.get();
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public Message getAttributeDisplayName(String name) {
+    return argDisplayMap.get(name);
+  }
 
   /**
    * {@inheritDoc}
diff --git a/opends/src/server/org/opends/server/tasks/InitializeTargetTask.java b/opends/src/server/org/opends/server/tasks/InitializeTargetTask.java
index e98356a..2b8b8d9 100644
--- a/opends/src/server/org/opends/server/tasks/InitializeTargetTask.java
+++ b/opends/src/server/org/opends/server/tasks/InitializeTargetTask.java
@@ -39,6 +39,7 @@
 import org.opends.server.backends.task.Task;
 import org.opends.server.backends.task.TaskState;
 import org.opends.messages.TaskMessages;
+import org.opends.messages.Message;
 import org.opends.server.protocols.asn1.ASN1OctetString;
 import org.opends.server.replication.plugin.ReplicationDomain;
 import org.opends.server.types.Attribute;
@@ -78,6 +79,13 @@
   /**
    * {@inheritDoc}
    */
+  public Message getDisplayName() {
+    return TaskMessages.INFO_TASK_INITIALIZE_TARGET_NAME.get();
+  }
+
+  /**
+   * {@inheritDoc}
+   */
   @Override public void initializeTask() throws DirectoryException
   {
     // FIXME -- Do we need any special authorization here?
diff --git a/opends/src/server/org/opends/server/tasks/InitializeTask.java b/opends/src/server/org/opends/server/tasks/InitializeTask.java
index 11a4fd8..5d5649f 100644
--- a/opends/src/server/org/opends/server/tasks/InitializeTask.java
+++ b/opends/src/server/org/opends/server/tasks/InitializeTask.java
@@ -80,6 +80,13 @@
   /**
    * {@inheritDoc}
    */
+  public Message getDisplayName() {
+    return TaskMessages.INFO_TASK_INITIALIZE_NAME.get();
+  }
+
+  /**
+   * {@inheritDoc}
+   */
   @Override public void initializeTask() throws DirectoryException
   {
     if (TaskState.isDone(getTaskState()))
diff --git a/opends/src/server/org/opends/server/tasks/LeaveLockdownModeTask.java b/opends/src/server/org/opends/server/tasks/LeaveLockdownModeTask.java
index d6433ee..0f2993c 100644
--- a/opends/src/server/org/opends/server/tasks/LeaveLockdownModeTask.java
+++ b/opends/src/server/org/opends/server/tasks/LeaveLockdownModeTask.java
@@ -50,6 +50,14 @@
 public class LeaveLockdownModeTask
        extends Task
 {
+
+  /**
+   * {@inheritDoc}
+   */
+  public Message getDisplayName() {
+    return INFO_TASK_LEAVE_LOCKDOWN_MODE_NAME.get();
+  }
+
   /**
    * {@inheritDoc}
    */
diff --git a/opends/src/server/org/opends/server/tasks/RebuildTask.java b/opends/src/server/org/opends/server/tasks/RebuildTask.java
index f2ca592..a8703e5 100644
--- a/opends/src/server/org/opends/server/tasks/RebuildTask.java
+++ b/opends/src/server/org/opends/server/tasks/RebuildTask.java
@@ -26,6 +26,7 @@
  */
 package org.opends.server.tasks;
 import org.opends.messages.Message;
+import org.opends.messages.TaskMessages;
 
 import org.opends.server.backends.task.Task;
 import org.opends.server.backends.task.TaskState;
@@ -95,6 +96,13 @@
   /**
    * {@inheritDoc}
    */
+  public Message getDisplayName() {
+    return TaskMessages.INFO_TASK_REBUILD_NAME.get();
+  }
+
+  /**
+   * {@inheritDoc}
+   */
   @Override public void initializeTask() throws DirectoryException
   {
     // If the client connection is available, then make sure the associated
diff --git a/opends/src/server/org/opends/server/tasks/RestoreTask.java b/opends/src/server/org/opends/server/tasks/RestoreTask.java
index 76bb5f3..b13858c 100644
--- a/opends/src/server/org/opends/server/tasks/RestoreTask.java
+++ b/opends/src/server/org/opends/server/tasks/RestoreTask.java
@@ -59,6 +59,8 @@
 import org.opends.server.types.ResultCode;
 
 import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
 import java.io.File;
 
 /**
@@ -73,6 +75,24 @@
   private static final DebugTracer TRACER = getTracer();
 
 
+  /**
+   * Stores mapping between configuration attribute name and its label.
+   */
+  static private Map<String,Message> argDisplayMap =
+          new HashMap<String,Message>();
+  static {
+    argDisplayMap.put(
+            ATTR_BACKUP_DIRECTORY_PATH,
+            INFO_RESTORE_ARG_BACKUP_DIR.get());
+
+    argDisplayMap.put(
+            ATTR_BACKUP_ID,
+            INFO_RESTORE_ARG_BACKUP_ID.get());
+
+    argDisplayMap.put(
+            ATTR_TASK_RESTORE_VERIFY_ONLY,
+            INFO_RESTORE_ARG_VERIFY_ONLY.get());
+  }
 
 
   // The task arguments.
@@ -84,6 +104,20 @@
   /**
    * {@inheritDoc}
    */
+  public Message getDisplayName() {
+    return INFO_TASK_RESTORE_NAME.get();
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public Message getAttributeDisplayName(String name) {
+    return argDisplayMap.get(name);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
   @Override public void initializeTask() throws DirectoryException
   {
     // If the client connection is available, then make sure the associated
diff --git a/opends/src/server/org/opends/server/tasks/SetGenerationIdTask.java b/opends/src/server/org/opends/server/tasks/SetGenerationIdTask.java
index ba4b078..a439155 100644
--- a/opends/src/server/org/opends/server/tasks/SetGenerationIdTask.java
+++ b/opends/src/server/org/opends/server/tasks/SetGenerationIdTask.java
@@ -32,6 +32,7 @@
 
 import org.opends.messages.MessageBuilder;
 import org.opends.messages.TaskMessages;
+import org.opends.messages.Message;
 import org.opends.server.backends.task.Task;
 import org.opends.server.backends.task.TaskState;
 import static org.opends.server.loggers.debug.DebugLogger.*;
@@ -75,6 +76,13 @@
   /**
    * {@inheritDoc}
    */
+  public Message getDisplayName() {
+    return TaskMessages.INFO_TASK_SET_GENERATION_ID_NAME.get();
+  }
+
+  /**
+   * {@inheritDoc}
+   */
   @Override public void initializeTask() throws DirectoryException
   {
     if (TaskState.isDone(getTaskState()))
diff --git a/opends/src/server/org/opends/server/tasks/ShutdownTask.java b/opends/src/server/org/opends/server/tasks/ShutdownTask.java
index 58cc374..ca42629 100644
--- a/opends/src/server/org/opends/server/tasks/ShutdownTask.java
+++ b/opends/src/server/org/opends/server/tasks/ShutdownTask.java
@@ -69,6 +69,12 @@
   private Message shutdownMessage;
 
 
+  /**
+   * {@inheritDoc}
+   */
+  public Message getDisplayName() {
+    return INFO_TASK_SHUTDOWN_NAME.get();
+  }
 
   /**
    * Performs any task-specific initialization that may be required before
diff --git a/opends/src/server/org/opends/server/tools/ManageTasks.java b/opends/src/server/org/opends/server/tools/ManageTasks.java
new file mode 100644
index 0000000..8f13d2f
--- /dev/null
+++ b/opends/src/server/org/opends/server/tools/ManageTasks.java
@@ -0,0 +1,926 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.tools;
+
+import org.opends.messages.Message;
+import static org.opends.messages.ToolMessages.*;
+import org.opends.server.api.ErrorLogPublisher;
+import org.opends.server.core.DirectoryServer;
+import static org.opends.server.loggers.ErrorLogger.removeErrorLogPublisher;
+import org.opends.server.protocols.asn1.ASN1Exception;
+import static org.opends.server.tools.ToolConstants.*;
+import org.opends.server.tools.tasks.TaskClient;
+import org.opends.server.tools.tasks.TaskEntry;
+import org.opends.server.types.LDAPException;
+import org.opends.server.util.StaticUtils;
+import static org.opends.server.util.StaticUtils.filterExitCode;
+import org.opends.server.util.args.ArgumentException;
+import org.opends.server.util.args.BooleanArgument;
+import org.opends.server.util.args.LDAPConnectionArgumentParser;
+import org.opends.server.util.args.StringArgument;
+import org.opends.server.util.cli.CLIException;
+import org.opends.server.util.cli.ConsoleApplication;
+import org.opends.server.util.cli.LDAPConnectionConsoleInteraction;
+import org.opends.server.util.cli.Menu;
+import org.opends.server.util.cli.MenuBuilder;
+import org.opends.server.util.cli.MenuCallback;
+import org.opends.server.util.cli.MenuResult;
+import org.opends.server.util.table.TableBuilder;
+import org.opends.server.util.table.TextTablePrinter;
+
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Tool for getting information and managing tasks in the Directory Server.
+ */
+public class ManageTasks extends ConsoleApplication {
+
+  private static ErrorLogPublisher errorLogPublisher = null;
+
+  /**
+   * The main method for TaskInfo tool.
+   *
+   * @param args The command-line arguments provided to this program.
+   */
+  public static void main(String[] args) {
+    int retCode = mainTaskInfo(args, System.in, System.out, System.err);
+
+    if (errorLogPublisher != null) {
+      removeErrorLogPublisher(errorLogPublisher);
+    }
+
+    if (retCode != 0) {
+      System.exit(filterExitCode(retCode));
+    }
+  }
+
+  /**
+   * Processes the command-line arguments and invokes the process for
+   * displaying task information.
+   *
+   * @param args The command-line arguments provided to this program.
+   * @return int return code
+   */
+  public static int mainTaskInfo(String[] args) {
+    return mainTaskInfo(args, System.in, System.out, System.err);
+  }
+
+  /**
+   * Processes the command-line arguments and invokes the export process.
+   *
+   * @param args             The command-line arguments provided to this
+   * @param in               Input stream from which to solicit user input.
+   * @param out              The output stream to use for standard output, or
+   *                         {@code null} if standard output is not needed.
+   * @param err              The output stream to use for standard error, or
+   *                         {@code null} if standard error is not needed.
+
+   * @return int return code
+   */
+  public static int mainTaskInfo(String[] args,
+                                 InputStream in,
+                                 OutputStream out,
+                                 OutputStream err) {
+    ManageTasks tool = new ManageTasks(in, out, err);
+    return tool.process(args);
+  }
+
+  private static final int INDENT = 2;
+
+  /**
+   * ID of task for which to display details and exit.
+   */
+  private StringArgument task = null;
+
+  /**
+   * Indicates print summary and exit.
+   */
+  private BooleanArgument summary = null;
+
+  /**
+   * ID of task to cancel.
+   */
+  private StringArgument cancel = null;
+
+  /**
+   * Argument used to request non-interactive behavior.
+   */
+  private BooleanArgument noPrompt = null;
+
+  /**
+   * Accesses the directory's task backend.
+   */
+  private TaskClient taskClient;
+
+  /**
+   * Constructs a parameterized instance.
+   *
+   * @param in               Input stream from which to solicit user input.
+   * @param out              The output stream to use for standard output, or
+   *                         {@code null} if standard output is not needed.
+   * @param err              The output stream to use for standard error, or
+   *                         {@code null} if standard error is not needed.
+   */
+  public ManageTasks(InputStream in, OutputStream out, OutputStream err) {
+    super(in, out, err);
+  }
+
+  /**
+   * Processes the command-line arguments and invokes the export process.
+   *
+   * @param args       The command-line arguments provided to this
+   *                   program.
+   * @return The error code.
+   */
+  public int process(String[] args) {
+
+    DirectoryServer.bootstrapClient();
+
+    // Create the command-line argument parser for use with this program.
+    LDAPConnectionArgumentParser argParser = new LDAPConnectionArgumentParser(
+            "org.opends.server.tools.TaskInfo",
+            INFO_TASKINFO_TOOL_DESCRIPTION.get(),
+            false);
+
+    // Initialize all the command-line argument types and register them with the
+    // parser.
+    try {
+
+      task = new StringArgument(
+              "task", 't', "task",
+              false, true, "{taskID}",
+              INFO_TASKINFO_TASK_ARG_DESCRIPTION.get());
+      argParser.addArgument(task);
+
+      cancel = new StringArgument(
+              "cancel", 'c', "cancel",
+              false, true, "{taskID}",
+              INFO_TASKINFO_TASK_ARG_CANCEL.get());
+      argParser.addArgument(cancel);
+
+      summary = new BooleanArgument(
+              "summary", 's', "summary",
+              INFO_TASKINFO_SUMMARY_ARG_DESCRIPTION.get());
+      argParser.addArgument(summary);
+
+      noPrompt = new BooleanArgument(
+              OPTION_LONG_NO_PROMPT,
+              OPTION_SHORT_NO_PROMPT,
+              OPTION_LONG_NO_PROMPT,
+              INFO_DESCRIPTION_NO_PROMPT.get());
+      argParser.addArgument(noPrompt);
+
+      BooleanArgument displayUsage = new BooleanArgument(
+              "help", OPTION_SHORT_HELP,
+              OPTION_LONG_HELP,
+              INFO_DESCRIPTION_USAGE.get());
+      argParser.addArgument(displayUsage);
+      argParser.setUsageArgument(displayUsage);
+    }
+    catch (ArgumentException ae) {
+      Message message = ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage());
+      println(message);
+      return 1;
+    }
+
+    // Parse the command-line arguments provided to this program.
+    try {
+      argParser.parseArguments(args);
+      StaticUtils.checkOnlyOneArgPresent(task, summary, cancel);
+    }
+    catch (ArgumentException ae) {
+      Message message = ERR_ERROR_PARSING_ARGS.get(ae.getMessage());
+      println(message);
+      println(argParser.getUsageMessage());
+      return 1;
+    }
+
+    if (!argParser.usageOrVersionDisplayed()) {
+      try {
+        LDAPConnectionConsoleInteraction ui =
+                new LDAPConnectionConsoleInteraction(
+                        this, argParser.getArguments());
+
+        taskClient = new TaskClient(argParser.connect(ui,
+                getOutputStream(), getErrorStream()));
+
+        if (isMenuDrivenMode()) {
+
+          // Keep prompting the user until they specify quit of
+          // there is a fatal exception
+          while (true) {
+            println();
+            Menu<Void> menu = getSummaryMenu();
+            MenuResult<Void> result = menu.run();
+            if (result.isQuit()) {
+              return 0;
+            }
+          }
+
+        } else if (task.isPresent()) {
+          println();
+          new PrintTaskInfo(task.getValue()).invoke(this);
+        } else if (summary.isPresent()) {
+          println();
+          printSummaryTable();
+        } else if (cancel.isPresent()) {
+          new CancelTask(cancel.getValue()).invoke(this);
+        }
+
+      } catch (LDAPConnectionException lce) {
+        println(INFO_TASKINFO_LDAP_EXCEPTION.get(lce.getMessageObject()));
+        return 1;
+      } catch (Exception e) {
+        println(Message.raw(e.getMessage()));
+        return 1;
+      }
+    }
+    return 0;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean isAdvancedMode() {
+    return false;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean isInteractive() {
+    return !noPrompt.isPresent();
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean isMenuDrivenMode() {
+    return !task.isPresent() && !cancel.isPresent() && !summary.isPresent();
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean isQuiet() {
+    return false;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean isScriptFriendly() {
+    return false;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean isVerbose() {
+    return false;
+  }
+
+  /**
+   * Creates the summary table.
+   *
+   * @throws IOException if there is a problem with screen I/O
+   * @throws LDAPException if there is a problem getting information
+   *         out to the directory
+   * @throws ASN1Exception if there is a problem with the encoding
+   */
+  private void printSummaryTable()
+          throws LDAPException, IOException, ASN1Exception {
+    List<TaskEntry> entries = taskClient.getTaskEntries();
+    if (entries.size() > 0) {
+      TableBuilder table = new TableBuilder();
+      Map<String, TaskEntry> mapIdToEntry =
+              new TreeMap<String, TaskEntry>();
+      for (TaskEntry entry : entries) {
+        String taskId = entry.getId();
+        if (taskId != null) {
+          mapIdToEntry.put(taskId, entry);
+        }
+      }
+
+      table.appendHeading(INFO_TASKINFO_FIELD_ID.get());
+      table.appendHeading(INFO_TASKINFO_FIELD_TYPE.get());
+      table.appendHeading(INFO_TASKINFO_FIELD_STATUS.get());
+      for (String taskId : mapIdToEntry.keySet()) {
+        TaskEntry entryWrapper = mapIdToEntry.get(taskId);
+        table.startRow();
+        table.appendCell(taskId);
+        table.appendCell(entryWrapper.getType());
+        table.appendCell(entryWrapper.getState());
+      }
+      StringWriter sw = new StringWriter();
+      TextTablePrinter tablePrinter = new TextTablePrinter(sw);
+      tablePrinter.setIndentWidth(INDENT);
+      tablePrinter.setTotalWidth(80);
+      table.print(tablePrinter);
+      println(Message.raw(sw.getBuffer()));
+    } else {
+      println(INFO_TASKINFO_NO_TASKS.get());
+      println();
+    }
+  }
+
+  /**
+   * Creates the summary table.
+   *
+   * @return list of strings of IDs of all the tasks in the table in order
+   *         of the indexes printed in the table
+   * @throws IOException if there is a problem with screen I/O
+   * @throws LDAPException if there is a problem getting information
+   *         out to the directory
+   * @throws ASN1Exception if there is a problem with the encoding
+   */
+  private Menu<Void> getSummaryMenu()
+          throws LDAPException, IOException, ASN1Exception {
+    List<String> taskIds = new ArrayList<String>();
+    List<Integer> cancelableIndices = new ArrayList<Integer>();
+    List<TaskEntry> entries = taskClient.getTaskEntries();
+    MenuBuilder<Void> menuBuilder = new MenuBuilder<Void>(this);
+    if (entries.size() > 0) {
+      Map<String, TaskEntry> mapIdToEntry =
+              new TreeMap<String, TaskEntry>();
+      for (TaskEntry entry : entries) {
+        String taskId = entry.getId();
+        if (taskId != null) {
+          mapIdToEntry.put(taskId, entry);
+        }
+      }
+
+      menuBuilder.setColumnHeadings(
+              INFO_TASKINFO_FIELD_ID.get(),
+              INFO_TASKINFO_FIELD_TYPE.get(),
+              INFO_TASKINFO_FIELD_STATUS.get());
+      menuBuilder.setColumnWidths(null, null, 0);
+      int index = 0;
+      for (final String taskId : mapIdToEntry.keySet()) {
+        taskIds.add(taskId);
+        final TaskEntry taskEntry = mapIdToEntry.get(taskId);
+        menuBuilder.addNumberedOption(
+                Message.raw(taskEntry.getId()),
+                new TaskDrilldownMenu(taskId),
+                taskEntry.getType(), taskEntry.getState());
+        index++;
+        if (taskEntry.isCancelable() && !taskEntry.isDone()) {
+          cancelableIndices.add(index);
+        }
+      }
+    } else {
+      // println();
+      println(INFO_TASKINFO_NO_TASKS.get());
+      println();
+    }
+
+    menuBuilder.addCharOption(
+            Message.raw("r"),
+            INFO_TASKINFO_CMD_REFRESH.get(),
+            new PrintSummaryTop());
+
+    if (cancelableIndices.size() > 0) {
+      menuBuilder.addCharOption(
+              Message.raw("c"),
+              INFO_TASKINFO_CMD_CANCEL.get(),
+              new CancelTaskTop(taskIds, cancelableIndices));
+    }
+    menuBuilder.addQuitOption();
+
+    return menuBuilder.toMenu();
+  }
+
+  /**
+   * Gets the client that can be used to interact with the task backend.
+   *
+   * @return TaskClient for interacting with the task backend.
+   */
+  public TaskClient getTaskClient() {
+    return this.taskClient;
+  }
+
+  /**
+   * Base for callbacks that implement top level menu items.
+   */
+  static abstract private class TopMenuCallback
+          implements MenuCallback<Void> {
+
+    /**
+     * {@inheritDoc}
+     */
+    public MenuResult<Void> invoke(ConsoleApplication app) throws CLIException {
+      return invoke((ManageTasks)app);
+    }
+
+    /**
+     * Called upon task invocation.
+     *
+     * @param app this console application
+     * @return MessageResult result of task
+     * @throws CLIException if there is a problem
+     */
+    protected abstract MenuResult<Void> invoke(ManageTasks app)
+            throws CLIException;
+
+  }
+
+  /**
+   * Base for callbacks that manage task entries.
+   */
+  static abstract private class TaskOperationCallback
+          implements MenuCallback<TaskEntry> {
+
+    /** ID of the task to manage. */
+    protected String taskId;
+
+    /**
+     * Constructs a parameterized instance.
+     *
+     * @param taskId if the task to examine
+     */
+    public TaskOperationCallback(String taskId) {
+      this.taskId = taskId;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public MenuResult<TaskEntry> invoke(ConsoleApplication app)
+            throws CLIException
+    {
+      return invoke((ManageTasks)app);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected abstract MenuResult<TaskEntry> invoke(ManageTasks app)
+            throws CLIException;
+
+  }
+
+  /**
+   * Executable for printing a task summary table.
+   */
+  static private class PrintSummaryTop extends TopMenuCallback {
+
+    public MenuResult<Void> invoke(ManageTasks app)
+            throws CLIException
+    {
+      // Since the summary table is reprinted every time the
+      // user enters the top level this task just returns
+      // 'success'
+      return MenuResult.success();
+    }
+  }
+
+  /**
+   * Exectuable for printing a particular task's details.
+   */
+  static private class TaskDrilldownMenu extends TopMenuCallback {
+
+    private String taskId;
+
+    /**
+     * Constructs a parameterized instance.
+     *
+     * @param taskId of the task for which information will be displayed
+     */
+    public TaskDrilldownMenu(String taskId) {
+      this.taskId = taskId;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public MenuResult<Void> invoke(ManageTasks app) throws CLIException {
+      MenuResult<TaskEntry> res = new PrintTaskInfo(taskId).invoke(app);
+      TaskEntry taskEntry = res.getValue();
+      if (taskEntry != null) {
+        while (true) {
+          try {
+            taskEntry = app.getTaskClient().getTaskEntry(taskId);
+
+            // Show the menu
+            MenuBuilder<TaskEntry> menuBuilder =
+                    new MenuBuilder<TaskEntry>(app);
+            menuBuilder.addBackOption(true);
+            menuBuilder.addCharOption(
+                    Message.raw("r"),
+                    INFO_TASKINFO_CMD_REFRESH.get(),
+                    new PrintTaskInfo(taskId));
+            List<Message> logs = taskEntry.getLogMessages();
+            if (logs != null && logs.size() > 0) {
+              menuBuilder.addCharOption(
+                      Message.raw("l"),
+                      INFO_TASKINFO_CMD_VIEW_LOGS.get(),
+                      new ViewTaskLogs(taskId));
+            }
+            if (taskEntry.isCancelable() && !taskEntry.isDone()) {
+              menuBuilder.addCharOption(
+                      Message.raw("c"),
+                      INFO_TASKINFO_CMD_CANCEL.get(),
+                      new CancelTask(taskId));
+            }
+            menuBuilder.addQuitOption();
+            Menu<TaskEntry> menu = menuBuilder.toMenu();
+            MenuResult<TaskEntry> result = menu.run();
+            if (result.isCancel()) {
+              break;
+            } else if (result.isQuit()) {
+              System.exit(0);
+            }
+          } catch (Exception e) {
+            app.println(Message.raw(e.getMessage()));
+          }
+        }
+      } else {
+        app.println(ERR_TASKINFO_UNKNOWN_TASK_ENTRY.get(taskId));
+      }
+      return MenuResult.success();
+    }
+
+  }
+
+  /**
+   * Exectuable for printing a particular task's details.
+   */
+  static private class PrintTaskInfo extends TaskOperationCallback {
+
+    /**
+     * Constructs a parameterized instance.
+     *
+     * @param taskId of the task for which information will be printed
+     */
+    public PrintTaskInfo(String taskId) {
+      super(taskId);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public MenuResult<TaskEntry> invoke(ManageTasks app)
+            throws CLIException
+    {
+      TaskEntry taskEntry = null;
+      try {
+        taskEntry = app.getTaskClient().getTaskEntry(taskId);
+
+        TableBuilder table = new TableBuilder();
+        table.appendHeading(INFO_TASKINFO_DETAILS.get());
+
+        table.startRow();
+        table.appendCell(INFO_TASKINFO_FIELD_ID.get());
+        table.appendCell(taskEntry.getId());
+
+        table.startRow();
+        table.appendCell(INFO_TASKINFO_FIELD_TYPE.get());
+        table.appendCell(taskEntry.getType());
+
+        table.startRow();
+        table.appendCell(INFO_TASKINFO_FIELD_STATUS.get());
+        table.appendCell(taskEntry.getState());
+
+        table.startRow();
+        table.appendCell(INFO_TASKINFO_FIELD_SCHEDULED_START.get());
+        Message m = taskEntry.getScheduledStartTime();
+        if (m == null || m.equals(Message.EMPTY)) {
+          table.appendCell(INFO_TASKINFO_IMMEDIATE_EXECUTION.get());
+        } else {
+          table.appendCell(m);
+        }
+
+        table.startRow();
+        table.appendCell(INFO_TASKINFO_FIELD_ACTUAL_START.get());
+        table.appendCell(taskEntry.getActualStartTime());
+
+        table.startRow();
+        table.appendCell(INFO_TASKINFO_FIELD_COMPLETION_TIME.get());
+        table.appendCell(taskEntry.getCompletionTime());
+
+        writeMultiValueCells(
+                table,
+                INFO_TASKINFO_FIELD_DEPENDENCY.get(),
+                taskEntry.getDependencyIds());
+
+        table.startRow();
+        table.appendCell(INFO_TASKINFO_FIELD_FAILED_DEPENDENCY_ACTION.get());
+        m = taskEntry.getFailedDependencyAction();
+        table.appendCell(m != null ? m : INFO_TASKINFO_NONE.get());
+
+        writeMultiValueCells(
+                table,
+                INFO_TASKINFO_FIELD_NOTIFY_ON_COMPLETION.get(),
+                taskEntry.getCompletionNotificationEmailAddresses(),
+                INFO_TASKINFO_NONE_SPECIFIED.get());
+
+        writeMultiValueCells(
+                table,
+                INFO_TASKINFO_FIELD_NOTIFY_ON_ERROR.get(),
+                taskEntry.getErrorNotificationEmailAddresses(),
+                INFO_TASKINFO_NONE_SPECIFIED.get());
+
+        StringWriter sw = new StringWriter();
+        TextTablePrinter tablePrinter = new TextTablePrinter(sw);
+        tablePrinter.setTotalWidth(80);
+        tablePrinter.setIndentWidth(INDENT);
+        tablePrinter.setColumnWidth(1, 0);
+        table.print(tablePrinter);
+        app.println();
+        app.println(Message.raw(sw.getBuffer().toString()));
+
+        // Create a table for the task options
+        table = new TableBuilder();
+        table.appendHeading(INFO_TASKINFO_OPTIONS.get(taskEntry.getType()));
+        Map<Message,List<String>> taskSpecificAttrs =
+                taskEntry.getTaskSpecificAttributeValuePairs();
+        for (Message attrName : taskSpecificAttrs.keySet()) {
+          table.startRow();
+          table.appendCell(attrName);
+          List<String> values = taskSpecificAttrs.get(attrName);
+          if (values.size() > 0) {
+            table.appendCell(values.get(0));
+          }
+          if (values.size() > 1) {
+            for (int i = 1; i < values.size(); i++) {
+              table.startRow();
+              table.appendCell();
+              table.appendCell(values.get(i));
+            }
+          }
+        }
+        sw = new StringWriter();
+        tablePrinter = new TextTablePrinter(sw);
+        tablePrinter.setTotalWidth(80);
+        tablePrinter.setIndentWidth(INDENT);
+        tablePrinter.setColumnWidth(1, 0);
+        table.print(tablePrinter);
+        app.println(Message.raw(sw.getBuffer().toString()));
+
+        // Print the last log message if any
+        List<Message> logs = taskEntry.getLogMessages();
+        if (logs != null && logs.size() > 0) {
+
+          // Create a table for the last log entry
+          table = new TableBuilder();
+          table.appendHeading(INFO_TASKINFO_FIELD_LAST_LOG.get());
+          table.startRow();
+          table.appendCell(logs.get(logs.size() - 1));
+
+          sw = new StringWriter();
+          tablePrinter = new TextTablePrinter(sw);
+          tablePrinter.setTotalWidth(80);
+          tablePrinter.setIndentWidth(INDENT);
+          tablePrinter.setColumnWidth(0, 0);
+          table.print(tablePrinter);
+          app.println(Message.raw(sw.getBuffer().toString()));
+        }
+
+        app.println();
+      } catch (Exception e) {
+        app.println(ERR_TASKINFO_RETRIEVING_TASK_ENTRY.get(
+                    taskId, e.getMessage()));
+      }
+      return MenuResult.success(taskEntry);
+    }
+
+    /**
+     * Writes an attribute and associated values to the table.
+     * @param table of task details
+     * @param fieldLabel of attribute
+     * @param values of the attribute
+     */
+    private void writeMultiValueCells(TableBuilder table,
+                                      Message fieldLabel,
+                                      List<?> values) {
+      writeMultiValueCells(table, fieldLabel, values, INFO_TASKINFO_NONE.get());
+    }
+
+    /**
+     * Writes an attribute and associated values to the table.
+     *
+     * @param table of task details
+     * @param fieldLabel of attribute
+     * @param values of the attribute
+     * @param noneLabel label for the value column when there are no values
+     */
+    private void writeMultiValueCells(TableBuilder table,
+                                      Message fieldLabel,
+                                      List<?> values,
+                                      Message noneLabel) {
+      table.startRow();
+      table.appendCell(fieldLabel);
+      if (values.size() == 0) {
+        table.appendCell(noneLabel);
+      } else if (values.size() > 0) {
+        table.appendCell(values.get(0));
+      }
+      if (values.size() > 1) {
+        for (int i = 1; i < values.size(); i++) {
+          table.startRow();
+          table.appendCell();
+          table.appendCell(values.get(i));
+        }
+      }
+    }
+  }
+
+  /**
+   * Exectuable for printing a particular task's details.
+   */
+  static private class ViewTaskLogs extends TaskOperationCallback {
+
+    /**
+     * Constructs a parameterized instance.
+     *
+     * @param taskId of the task for which log records will be printed
+     */
+    public ViewTaskLogs(String taskId) {
+      super(taskId);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected MenuResult<TaskEntry> invoke(ManageTasks app)
+            throws CLIException
+    {
+      TaskEntry taskEntry = null;
+      try {
+        taskEntry = app.getTaskClient().getTaskEntry(taskId);
+        List<Message> logs = taskEntry.getLogMessages();
+        app.println();
+
+        // Create a table for the last log entry
+        TableBuilder table = new TableBuilder();
+        table.appendHeading(INFO_TASKINFO_FIELD_LOG.get());
+        if (logs != null && logs.size() > 0) {
+          for (Message log : logs) {
+            table.startRow();
+            table.appendCell(log);
+          }
+        } else {
+          table.startRow();
+          table.appendCell(INFO_TASKINFO_NONE.get());
+        }
+        StringWriter sw = new StringWriter();
+        TextTablePrinter tablePrinter = new TextTablePrinter(sw);
+        tablePrinter.setTotalWidth(80);
+        tablePrinter.setIndentWidth(INDENT);
+        tablePrinter.setColumnWidth(0, 0);
+        table.print(tablePrinter);
+        app.println(Message.raw(sw.getBuffer().toString()));
+        app.println();
+      } catch (Exception e) {
+        app.println(ERR_TASKINFO_ACCESSING_LOGS.get(taskId, e.getMessage()));
+      }
+      return MenuResult.success(taskEntry);
+    }
+  }
+
+  /**
+   * Executable for canceling a particular task.
+   */
+  static private class CancelTaskTop extends TopMenuCallback {
+
+    private List<String> taskIds;
+    private List<Integer> cancelableIndices;
+
+    /**
+     * Constructs a parameterized instance.
+     *
+     * @param taskIds of all known tasks
+     * @param cancelableIndices list of integers whose elements represent
+     *        the indices of <code>taskIds</code> that are cancelable
+     */
+    public CancelTaskTop(List<String> taskIds,
+                         List<Integer> cancelableIndices) {
+      this.taskIds = taskIds;
+      this.cancelableIndices = cancelableIndices;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public MenuResult<Void> invoke(ManageTasks app)
+            throws CLIException
+    {
+      if (taskIds != null && taskIds.size() > 0) {
+        if (cancelableIndices != null && cancelableIndices.size() > 0) {
+
+          // Prompt for the task number
+          Integer index = null;
+          String line = app.readLineOfInput(
+                  INFO_TASKINFO_CMD_CANCEL_NUMBER_PROMPT.get(
+                          cancelableIndices.get(0)));
+          if (line.length() == 0) {
+            line = String.valueOf(cancelableIndices.get(0));
+          }
+          try {
+            int i = Integer.parseInt(line);
+            if (!cancelableIndices.contains(i)) {
+              app.println(ERR_TASKINFO_NOT_CANCELABLE_TASK_INDEX.get(i));
+            } else {
+              index = i - 1;
+            }
+          } catch (NumberFormatException nfe) {
+            // ignore;
+          }
+          if (index != null) {
+            String taskId = taskIds.get(index);
+            try {
+              CancelTask ct = new CancelTask(taskId);
+              MenuResult<TaskEntry> result = ct.invoke(app);
+              if (result.isSuccess()) {
+                return MenuResult.success();
+              } else {
+                return MenuResult.again();
+              }
+            } catch (Exception e) {
+              app.println(ERR_TASKINFO_CANCELING_TASK.get(
+                          taskId, e.getMessage()));
+              return MenuResult.again();
+            }
+          } else {
+            app.println(ERR_TASKINFO_INVALID_MENU_KEY.get(line));
+            return MenuResult.again();
+          }
+        } else {
+          app.println(INFO_TASKINFO_NO_CANCELABLE_TASKS.get());
+          return MenuResult.cancel();
+        }
+      } else {
+        app.println(INFO_TASKINFO_NO_TASKS.get());
+        return MenuResult.cancel();
+      }
+    }
+
+  }
+
+  /**
+   * Executable for canceling a particular task.
+   */
+  static private class CancelTask extends TaskOperationCallback {
+
+    /**
+     * Constructs a parameterized instance.
+     *
+     * @param taskId of the task to cancel
+     */
+    public CancelTask(String taskId) {
+      super(taskId);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public MenuResult<TaskEntry> invoke(ManageTasks app)
+            throws CLIException
+    {
+      try {
+        TaskEntry entry = app.getTaskClient().cancelTask(taskId);
+        app.println(INFO_TASKINFO_CMD_CANCEL_SUCCESS.get(taskId));
+        return MenuResult.success(entry);
+      } catch (Exception e) {
+        app.println(ERR_TASKINFO_CANCELING_TASK.get(
+                taskId, e.getMessage()));
+        return MenuResult.again();
+      }
+    }
+
+  }
+
+}
diff --git a/opends/src/server/org/opends/server/tools/tasks/TaskClient.java b/opends/src/server/org/opends/server/tools/tasks/TaskClient.java
new file mode 100644
index 0000000..b9972a1
--- /dev/null
+++ b/opends/src/server/org/opends/server/tools/tasks/TaskClient.java
@@ -0,0 +1,378 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.tools.tasks;
+
+import org.opends.messages.Message;
+import static org.opends.messages.ToolMessages.*;
+import org.opends.server.config.ConfigConstants;
+import static org.opends.server.config.ConfigConstants.*;
+import org.opends.server.protocols.asn1.ASN1Exception;
+import org.opends.server.protocols.asn1.ASN1OctetString;
+import org.opends.server.protocols.ldap.AddRequestProtocolOp;
+import org.opends.server.protocols.ldap.AddResponseProtocolOp;
+import org.opends.server.protocols.ldap.LDAPAttribute;
+import org.opends.server.protocols.ldap.LDAPConstants;
+import org.opends.server.protocols.ldap.LDAPControl;
+import org.opends.server.protocols.ldap.LDAPFilter;
+import org.opends.server.protocols.ldap.LDAPMessage;
+import org.opends.server.protocols.ldap.LDAPModification;
+import org.opends.server.protocols.ldap.LDAPResultCode;
+import org.opends.server.protocols.ldap.ModifyRequestProtocolOp;
+import org.opends.server.protocols.ldap.ModifyResponseProtocolOp;
+import org.opends.server.protocols.ldap.SearchRequestProtocolOp;
+import org.opends.server.protocols.ldap.SearchResultEntryProtocolOp;
+import org.opends.server.tools.LDAPConnection;
+import org.opends.server.tools.LDAPReader;
+import org.opends.server.tools.LDAPWriter;
+import org.opends.server.types.DereferencePolicy;
+import org.opends.server.types.Entry;
+import org.opends.server.types.LDAPException;
+import org.opends.server.types.ModificationType;
+import org.opends.server.types.RawAttribute;
+import org.opends.server.types.RawModification;
+import org.opends.server.types.SearchResultEntry;
+import org.opends.server.types.SearchScope;
+import static org.opends.server.types.ResultCode.*;
+import org.opends.server.backends.task.TaskState;
+
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Helper class for interacting with the task backend on behalf of utilities
+ * that are capable of being scheduled.
+ */
+public class TaskClient {
+
+  /**
+   * Connection through which task scheduling will take place.
+   */
+  protected LDAPConnection connection;
+
+  /**
+   * Keeps track of message IDs.
+   */
+  private AtomicInteger nextMessageID = new AtomicInteger(0);
+
+  /**
+   * Creates a new TaskClient for interacting with the task backend remotely.
+   * @param conn for accessing the task backend
+   */
+  public TaskClient(LDAPConnection conn) {
+    this.connection = conn;
+  }
+
+  /**
+   * Schedule a task for execution by writing an entry to the task backend.
+   *
+   * @param information to be scheduled
+   * @return String task ID assigned the new task
+   * @throws IOException if there is a stream communication problem
+   * @throws LDAPException if there is a problem getting information
+   *         out to the directory
+   * @throws ASN1Exception if there is a problem with the encoding
+   */
+  public synchronized String schedule(TaskScheduleInformation information)
+          throws LDAPException, IOException, ASN1Exception
+  {
+    LDAPReader reader = connection.getLDAPReader();
+    LDAPWriter writer = connection.getLDAPWriter();
+
+    // Use a formatted time/date for the ID so that is remotely useful
+    SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmssMM");
+    String taskID = df.format(new Date());
+
+    ASN1OctetString entryDN =
+         new ASN1OctetString(ATTR_TASK_ID + "=" + taskID + "," +
+                             SCHEDULED_TASK_BASE_RDN + "," + DN_TASK_ROOT);
+
+    ArrayList<LDAPControl> controls = new ArrayList<LDAPControl>();
+
+    ArrayList<RawAttribute> attributes = new ArrayList<RawAttribute>();
+
+    ArrayList<ASN1OctetString> ocValues = new ArrayList<ASN1OctetString>(3);
+    ocValues.add(new ASN1OctetString("top"));
+    ocValues.add(new ASN1OctetString(ConfigConstants.OC_TASK));
+    ocValues.add(new ASN1OctetString(information.getTaskObjectclass()));
+    attributes.add(new LDAPAttribute(ATTR_OBJECTCLASS, ocValues));
+
+    ArrayList<ASN1OctetString> taskIDValues = new ArrayList<ASN1OctetString>(1);
+    taskIDValues.add(new ASN1OctetString(taskID));
+    attributes.add(new LDAPAttribute(ATTR_TASK_ID, taskIDValues));
+
+    ArrayList<ASN1OctetString> classValues = new ArrayList<ASN1OctetString>(1);
+    classValues.add(new ASN1OctetString(information.getTaskClass().getName()));
+    attributes.add(new LDAPAttribute(ATTR_TASK_CLASS, classValues));
+
+    information.addTaskAttributes(attributes);
+
+    AddRequestProtocolOp addRequest = new AddRequestProtocolOp(entryDN,
+                                                               attributes);
+    LDAPMessage requestMessage =
+         new LDAPMessage(nextMessageID.getAndIncrement(), addRequest, controls);
+
+    // Send the request to the server and read the response.
+    LDAPMessage responseMessage;
+    writer.writeMessage(requestMessage);
+
+    responseMessage = reader.readMessage();
+    if (responseMessage == null)
+    {
+      throw new LDAPException(
+              LDAPResultCode.CLIENT_SIDE_SERVER_DOWN,
+              ERR_TASK_CLIENT_UNEXPECTED_CONNECTION_CLOSURE.get());
+    }
+
+    if (responseMessage.getProtocolOpType() !=
+        LDAPConstants.OP_TYPE_ADD_RESPONSE)
+    {
+      throw new LDAPException(
+              LDAPResultCode.CLIENT_SIDE_LOCAL_ERROR,
+              ERR_TASK_CLIENT_INVALID_RESPONSE_TYPE.get(
+                responseMessage.getProtocolOpName()));
+    }
+
+    AddResponseProtocolOp addResponse =
+         responseMessage.getAddResponseProtocolOp();
+    Message errorMessage = addResponse.getErrorMessage();
+    if (errorMessage != null) {
+      throw new LDAPException(
+              LDAPResultCode.CLIENT_SIDE_LOCAL_ERROR,
+              errorMessage);
+    }
+    return taskID;
+  }
+
+  /**
+   * Gets all the ds-task entries from the task root.
+   *
+   * @return list of entries from the task root
+   * @throws IOException if there is a stream communication problem
+   * @throws LDAPException if there is a problem getting information
+   *         out to the directory
+   * @throws ASN1Exception if there is a problem with the encoding
+   */
+  public synchronized List<TaskEntry> getTaskEntries()
+          throws LDAPException, IOException, ASN1Exception {
+    List<Entry> entries = new ArrayList<Entry>();
+
+    writeSearch(new SearchRequestProtocolOp(
+            new ASN1OctetString(ConfigConstants.DN_TASK_ROOT),
+            SearchScope.WHOLE_SUBTREE,
+            DereferencePolicy.NEVER_DEREF_ALIASES,
+            Integer.MAX_VALUE,
+            Integer.MAX_VALUE,
+            false,
+            LDAPFilter.decode("(objectclass=ds-task)"),
+            new LinkedHashSet<String>()));
+
+    LDAPReader reader = connection.getLDAPReader();
+    byte opType;
+    do {
+      LDAPMessage responseMessage = reader.readMessage();
+      if (responseMessage == null) {
+        throw new LDAPException(
+                LDAPResultCode.CLIENT_SIDE_SERVER_DOWN,
+                ERR_TASK_CLIENT_UNEXPECTED_CONNECTION_CLOSURE.get());
+      } else {
+        opType = responseMessage.getProtocolOpType();
+        if (opType == LDAPConstants.OP_TYPE_SEARCH_RESULT_ENTRY) {
+          SearchResultEntryProtocolOp searchEntryOp =
+                  responseMessage.getSearchResultEntryProtocolOp();
+          SearchResultEntry entry = searchEntryOp.toSearchResultEntry();
+          entries.add(entry);
+        }
+      }
+    }
+    while (opType != LDAPConstants.OP_TYPE_SEARCH_RESULT_DONE);
+    List<TaskEntry> taskEntries = new ArrayList<TaskEntry>(entries.size());
+    for (Entry entry : entries) {
+      taskEntries.add(new TaskEntry(entry));
+    }
+    return Collections.unmodifiableList(taskEntries);
+  }
+
+  /**
+   * Gets the entry of the task whose ID is <code>id</code> from the directory.
+   *
+   * @param id of the entry to retrieve
+   * @return Entry for the task
+   * @throws IOException if there is a stream communication problem
+   * @throws LDAPException if there is a problem getting information
+   *         out to the directory
+   * @throws ASN1Exception if there is a problem with the encoding
+   * @throws TaskClientException if there is no task with the requested id
+   */
+  public synchronized TaskEntry getTaskEntry(String id)
+          throws LDAPException, IOException, ASN1Exception, TaskClientException
+  {
+    Entry entry = null;
+
+    writeSearch(new SearchRequestProtocolOp(
+            new ASN1OctetString(ConfigConstants.DN_TASK_ROOT),
+            SearchScope.WHOLE_SUBTREE,
+            DereferencePolicy.NEVER_DEREF_ALIASES,
+            Integer.MAX_VALUE,
+            Integer.MAX_VALUE,
+            false,
+            LDAPFilter.decode("(ds-task-id=" + id + ")"),
+            new LinkedHashSet<String>()));
+
+    LDAPReader reader = connection.getLDAPReader();
+    byte opType;
+    do {
+      LDAPMessage responseMessage = reader.readMessage();
+      if (responseMessage == null) {
+        Message message = ERR_TASK_CLIENT_UNEXPECTED_CONNECTION_CLOSURE.get();
+        throw new LDAPException(UNAVAILABLE.getIntValue(), message);
+      } else {
+        opType = responseMessage.getProtocolOpType();
+        if (opType == LDAPConstants.OP_TYPE_SEARCH_RESULT_ENTRY) {
+          SearchResultEntryProtocolOp searchEntryOp =
+                  responseMessage.getSearchResultEntryProtocolOp();
+          entry = searchEntryOp.toSearchResultEntry();
+        }
+      }
+    }
+    while (opType != LDAPConstants.OP_TYPE_SEARCH_RESULT_DONE);
+    if (entry == null) {
+      throw new TaskClientException(ERR_TASK_CLIENT_UNKNOWN_TASK.get(id));
+    }
+    return new TaskEntry(entry);
+  }
+
+
+  /**
+   * Changes that the state of the task in the backend to a canceled state.
+   *
+   * @param  id if the task to cancel
+   * @return Entry of the task before the modification
+   * @throws IOException if there is a stream communication problem
+   * @throws LDAPException if there is a problem getting information
+   *         out to the directory
+   * @throws ASN1Exception if there is a problem with the encoding
+   * @throws TaskClientException if there is no task with the requested id
+   */
+  public synchronized TaskEntry cancelTask(String id)
+          throws TaskClientException, IOException, ASN1Exception, LDAPException
+  {
+    LDAPReader reader = connection.getLDAPReader();
+    LDAPWriter writer = connection.getLDAPWriter();
+
+    TaskEntry entry = getTaskEntry(id);
+    TaskState state = entry.getTaskState();
+    if (state != null) {
+      if (!TaskState.isDone(state)) {
+
+        ASN1OctetString dn = new ASN1OctetString(entry.getDN().toString());
+
+        ArrayList<RawModification> mods = new ArrayList<RawModification>();
+
+        ArrayList<ASN1OctetString> values = new ArrayList<ASN1OctetString>();
+        String newState;
+        if (TaskState.isPending(state)) {
+          newState = TaskState.CANCELED_BEFORE_STARTING.name();
+        } else {
+          newState = TaskState.STOPPED_BY_ADMINISTRATOR.name();
+        }
+        values.add(new ASN1OctetString(newState));
+        LDAPAttribute attr = new LDAPAttribute(ATTR_TASK_STATE, values);
+        mods.add(new LDAPModification(ModificationType.REPLACE, attr));
+
+        // We have to reset the start time or the scheduler will
+        // reschedule to task.
+        // attr = new LDAPAttribute(ATTR_TASK_SCHEDULED_START_TIME);
+        // mods.add(new LDAPModification(ModificationType.DELETE, attr));
+
+        ModifyRequestProtocolOp modRequest =
+                new ModifyRequestProtocolOp(dn, mods);
+        LDAPMessage requestMessage =
+             new LDAPMessage(nextMessageID.getAndIncrement(), modRequest, null);
+
+        writer.writeMessage(requestMessage);
+
+        LDAPMessage responseMessage = reader.readMessage();
+
+        if (responseMessage == null) {
+          Message message = ERR_TASK_CLIENT_UNEXPECTED_CONNECTION_CLOSURE.get();
+          throw new LDAPException(UNAVAILABLE.getIntValue(), message);
+        }
+
+        if (responseMessage.getProtocolOpType() !=
+                LDAPConstants.OP_TYPE_MODIFY_RESPONSE)
+        {
+          throw new LDAPException(
+                  LDAPResultCode.CLIENT_SIDE_LOCAL_ERROR,
+                  ERR_TASK_CLIENT_INVALID_RESPONSE_TYPE.get(
+                    responseMessage.getProtocolOpName()));
+        }
+
+        ModifyResponseProtocolOp modResponse =
+                responseMessage.getModifyResponseProtocolOp();
+        Message errorMessage = modResponse.getErrorMessage();
+        if (errorMessage != null) {
+          throw new LDAPException(
+                  LDAPResultCode.CLIENT_SIDE_LOCAL_ERROR,
+                  errorMessage);
+        }
+      } else {
+        throw new TaskClientException(
+                ERR_TASK_CLIENT_UNCANCELABLE_TASK.get(id));
+      }
+    } else {
+      throw new TaskClientException(
+              ERR_TASK_CLIENT_TASK_STATE_UNKNOWN.get(id));
+    }
+    return getTaskEntry(id);
+  }
+
+
+  /**
+   * Writes a search to the directory writer.
+   * @param searchRequest to write
+   * @throws IOException if there is a stream communication problem
+   */
+  private void writeSearch(SearchRequestProtocolOp searchRequest)
+          throws IOException {
+    LDAPWriter writer = connection.getLDAPWriter();
+    LDAPMessage requestMessage = new LDAPMessage(
+            nextMessageID.getAndIncrement(),
+            searchRequest,
+            new ArrayList<LDAPControl>());
+
+    // Send the request to the server and read the response.
+    writer.writeMessage(requestMessage);
+  }
+
+}
diff --git a/opends/src/server/org/opends/server/tools/tasks/TaskClientException.java b/opends/src/server/org/opends/server/tools/tasks/TaskClientException.java
new file mode 100644
index 0000000..08e0b32
--- /dev/null
+++ b/opends/src/server/org/opends/server/tools/tasks/TaskClientException.java
@@ -0,0 +1,84 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.tools.tasks;
+
+import org.opends.messages.Message;
+
+import org.opends.server.types.OpenDsException;
+
+/**
+ * Exception for problems related to interacting with the task backend.
+ */
+public class TaskClientException extends OpenDsException {
+
+  private static final long serialVersionUID = 3800881643050096416L;
+
+  /**
+   * Constructs a default instance.
+   */
+  public TaskClientException() {
+  }
+
+  /**
+   * Constructs a parameterized instance.
+   *
+   * @param cause of this exception
+   */
+  public TaskClientException(OpenDsException cause) {
+    super(cause);
+  }
+
+  /**
+   * Constructs a parameterized instance.
+   *
+   * @param message for this exception
+   */
+  public TaskClientException(Message message) {
+    super(message);
+  }
+
+  /**
+   * Constructs a parameterized instance.
+   *
+   * @param cause of this exception
+   */
+  public TaskClientException(Throwable cause) {
+    super(cause);
+  }
+
+  /**
+   * Constructs a parameterized instance.
+   *
+   * @param message for this exception
+   * @param cause of this exception
+   */
+  public TaskClientException(Message message, Throwable cause) {
+    super(message, cause);
+  }
+
+}
diff --git a/opends/src/server/org/opends/server/tools/tasks/TaskEntry.java b/opends/src/server/org/opends/server/tools/tasks/TaskEntry.java
new file mode 100644
index 0000000..4f622b1
--- /dev/null
+++ b/opends/src/server/org/opends/server/tools/tasks/TaskEntry.java
@@ -0,0 +1,482 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.tools.tasks;
+
+import org.opends.messages.Message;
+
+import org.opends.server.backends.task.Task;
+import org.opends.server.backends.task.TaskState;
+import org.opends.server.backends.task.FailedDependencyAction;
+import org.opends.server.types.Entry;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.DN;
+import static org.opends.server.util.ServerConstants.*;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.List;
+import java.util.LinkedHashSet;
+import java.util.ArrayList;
+import java.util.TimeZone;
+import java.util.Date;
+import java.util.Collections;
+import java.lang.reflect.Method;
+import java.text.SimpleDateFormat;
+import java.text.DateFormat;
+import java.text.ParseException;
+
+/**
+ * Processes information from a task entry from the directory and
+ * provides accessors for attribute information.  In some cases the
+ * data is formatted into more human-friendly formats.
+ */
+public class TaskEntry {
+
+  private static Map<String, Message> mapClassToTypeName =
+          new HashMap<String, Message>();
+
+  private static Map<String, Message> mapAttrToDisplayName =
+          new HashMap<String, Message>();
+
+
+  // These attributes associated with the ds-task object
+  // class are all handled explicitly below in the constructor
+  private static Set<String> supAttrNames = new HashSet<String>();
+  static {
+    supAttrNames.add("ds-task-id");
+    supAttrNames.add("ds-task-class-name");
+    supAttrNames.add("ds-task-state");
+    supAttrNames.add("ds-task-scheduled-start-time");
+    supAttrNames.add("ds-task-actual-start-time");
+    supAttrNames.add("ds-task-completion-time");
+    supAttrNames.add("ds-task-dependency-id");
+    supAttrNames.add("ds-task-failed-dependency-action");
+    supAttrNames.add("ds-task-log-message");
+    supAttrNames.add("ds-task-notify-on-completion");
+    supAttrNames.add("ds-task-notify-on-error");
+  }
+
+  private String id;
+  private String className;
+  private String state;
+  private String schedStart;
+  private String actStart;
+  private String compTime;
+  private List<String> depends;
+  private String depFailAct;
+  private List<String> logs;
+  private List<String> notifyComp;
+  private List<String> notifyErr;
+  private DN dn;
+
+  /**
+   * Task of the same type that implements.  Used for obtaining
+   * task name and attribute display information.
+   */
+  private Task task;
+
+  private Map<Message, List<String>> taskSpecificAttrValues =
+          new HashMap<Message, List<String>>();
+
+  /**
+   * Creates a parameterized instance.
+   *
+   * @param entry to wrap
+   */
+  public TaskEntry(Entry entry) {
+    dn = entry.getDN();
+
+    String p = "ds-task-";
+    id =         getSingleStringValue(entry, p + "id");
+    className =  getSingleStringValue(entry, p + "class-name");
+    state =      getSingleStringValue(entry, p + "state");
+    schedStart = getSingleStringValue(entry, p + "scheduled-start-time");
+    actStart =   getSingleStringValue(entry, p + "actual-start-time");
+    compTime =   getSingleStringValue(entry, p + "completion-time");
+    depends =    getMultiStringValue(entry,  p + "dependency-id");
+    depFailAct = getSingleStringValue(entry, p + "failed-dependency-action");
+    logs =       getMultiStringValue(entry,  p + "log-message");
+    notifyErr =  getMultiStringValue(entry,  p + "notify-on-completion");
+    notifyComp = getMultiStringValue(entry,  p + "notify-on-error");
+
+
+    // Build a map of non-superior attribute value pairs for display
+    Map<AttributeType, List<Attribute>> attrMap = entry.getUserAttributes();
+    for (AttributeType type : attrMap.keySet()) {
+      String typeName = type.getNormalizedPrimaryName();
+
+      // See if we've handled it already above
+      if (!supAttrNames.contains(typeName)) {
+        Message attrTypeName = getAttributeDisplayName(
+                type.getNormalizedPrimaryName());
+        List<Attribute> attrList = entry.getUserAttribute(type);
+        for (Attribute attr : attrList) {
+          LinkedHashSet<AttributeValue> valuesSet = attr.getValues();
+          for (AttributeValue av : valuesSet) {
+            List<String> valueList = taskSpecificAttrValues.get(attrTypeName);
+            if (valueList == null) {
+              valueList = new ArrayList<String>();
+              taskSpecificAttrValues.put(attrTypeName, valueList);
+            }
+            valueList.add(av.getStringValue());
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * Gets the DN of the wrapped entry.
+   *
+   * @return DN of entry
+   */
+  public DN getDN() {
+    return dn;
+  }
+
+  /**
+   * Gets the ID of the task.
+   *
+   * @return String ID of the task
+   */
+  public String getId() {
+    return id;
+  }
+
+  /**
+   * Gets the name of the class implementing the task represented here.
+   *
+   * @return String name of class
+   */
+  public String getClassName() {
+    return className;
+  }
+
+  /**
+   * Gets the state of the task.
+   *
+   * @return Message representing state
+   */
+  public Message getState() {
+    Message m = Message.EMPTY;
+    if (state != null) {
+      TaskState ts = TaskState.fromString(state);
+      if (ts != null) {
+        m = ts.getDisplayName();
+      }
+    }
+    return m;
+  }
+
+  /**
+   * Gets the human-friendly scheduled time.
+   *
+   * @return String time
+   */
+  public Message getScheduledStartTime() {
+    return formatTimeString(schedStart);
+  }
+
+  /**
+   * Gets the human-friendly start time.
+   *
+   * @return String time
+   */
+  public Message getActualStartTime() {
+    return formatTimeString(actStart);
+  }
+
+  /**
+   * Gets the human-friendly completion time.
+   *
+   * @return String time
+   */
+  public Message getCompletionTime() {
+    return formatTimeString(compTime);
+  }
+
+  /**
+   * Gets the IDs of tasks upon which this task depends.
+   *
+   * @return array of IDs
+   */
+  public List<String> getDependencyIds() {
+    return Collections.unmodifiableList(depends);
+  }
+
+  /**
+   * Gets the action to take if this task fails.
+   *
+   * @return String action
+   */
+  public Message getFailedDependencyAction() {
+    Message m = null;
+    if (depFailAct != null) {
+      FailedDependencyAction fda =
+              FailedDependencyAction.fromString(depFailAct);
+      if (fda != null) {
+        m = fda.getDisplayName();
+      }
+    }
+    return m;
+  }
+
+  /**
+   * Gets the logs associated with this task's execution.
+   *
+   * @return array of log messages
+   */
+  public List<Message> getLogMessages() {
+    List<Message> formattedLogs = new ArrayList<Message>();
+    for (String aLog : logs) {
+      formattedLogs.add(Message.raw(formatLogMessage(aLog)));
+    }
+    return Collections.unmodifiableList(formattedLogs);
+  }
+
+  /**
+   * Gets the email messages that will be used for notifications
+   * when the task completes.
+   *
+   * @return array of email addresses
+   */
+  public List<String> getCompletionNotificationEmailAddresses() {
+    return Collections.unmodifiableList(notifyComp);
+  }
+
+  /**
+   * Gets the email messages that will be used for notifications
+   * when the task encounters an error.
+   *
+   * @return array of email addresses
+   */
+  public List<String> getErrorNotificationEmailAddresses() {
+    return Collections.unmodifiableList(notifyErr);
+  }
+
+  /**
+   * Gets a user presentable string indicating the type of this task.
+   *
+   * @return Message type
+   */
+  public Message getType() {
+    Message type = Message.EMPTY;
+    if (className != null) {
+      type = mapClassToTypeName.get(className);
+      if (type == null) {
+        Task task = getTask();
+        if (task != null) {
+          try {
+            Method m = Task.class.getMethod("getDisplayName");
+            Object oName = m.invoke(task);
+            if (oName instanceof Message) {
+              mapClassToTypeName.put(className, (Message) oName);
+              type = (Message) oName;
+            }
+          } catch (Exception e) {
+            // ignore; this is best effort
+          }
+        }
+      }
+
+      // If we still can't get the type just resort
+      // to the class displayName
+      if (type == null) {
+        type = Message.raw(className);
+      }
+    }
+    return type;
+  }
+
+  /**
+   * Indicates whether or not this task supports a cancel operation.
+   *
+   * @return boolean where true means this task supports being canceled.
+   */
+  public boolean isCancelable() {
+    boolean cancelable = false;
+    TaskState state = getTaskState();
+    if (state != null) {
+      Task task = getTask();
+      cancelable = (TaskState.isPending(state) ||
+              (TaskState.isRunning(state) &&
+                      task != null &&
+                      task.isInterruptable()));
+    }
+    return cancelable;
+  }
+
+  /**
+   * Gets a mapping of attributes that are specific to the implementing
+   * task as opposed to the superior, or base, task.
+   *
+   * @return mapping of atribute field labels to lists of string values for
+   *         each field.
+   */
+  public Map<Message, List<String>> getTaskSpecificAttributeValuePairs() {
+    return taskSpecificAttrValues;
+  }
+
+  /**
+   * Gets the task state.
+   *
+   * @return TaskState of task
+   */
+  public TaskState getTaskState() {
+    TaskState ts = null;
+    if (state != null) {
+      ts = TaskState.fromString(state);
+    }
+    return ts;
+  }
+
+  /**
+   * Indicates whether or not this task is done.
+   *
+   * @return boolean where true means this task is done
+   */
+  public boolean isDone() {
+    TaskState ts = getTaskState();
+    return ts != null && TaskState.isDone(ts);
+  }
+
+  private String getSingleStringValue(Entry entry, String attrName) {
+    List<Attribute> attrList = entry.getAttribute(attrName);
+    if (attrList != null && attrList.size() == 1) {
+      Set<AttributeValue> values = attrList.get(0).getValues();
+      if (values != null && values.size() == 1) {
+        return values.iterator().next().getStringValue();
+      }
+    }
+    return "";
+  }
+
+  private List<String> getMultiStringValue(Entry entry, String attrName) {
+    List<String> valuesList = new ArrayList<String>();
+    List<Attribute> attrList = entry.getAttribute(attrName);
+    if (attrList != null) {
+      for (Attribute attr : attrList) {
+        Set<AttributeValue> values = attr.getValues();
+        if (values != null) {
+          for (AttributeValue value : values) {
+            valuesList.add(value.getStringValue());
+          }
+        }
+      }
+    }
+    return valuesList;
+  }
+
+  private Message getAttributeDisplayName(String attrName) {
+    Message name = mapAttrToDisplayName.get(attrName);
+    if (name == null) {
+      Task task = getTask();
+      if (task != null) {
+        try {
+          Method m = Task.class.getMethod(
+                  "getAttributeDisplayName", String.class);
+          Object o = m.invoke(task, attrName);
+          if (o != null && Message.class.isAssignableFrom(o.getClass())) {
+            name= (Message)o;
+            mapAttrToDisplayName.put(attrName, name);
+          }
+        } catch (Exception e) {
+          // ignore
+        }
+      }
+    }
+    if (name == null) {
+      name = Message.raw(attrName);
+    }
+    return name;
+  }
+
+  /**
+   * Formats a log message for appending to the table.
+   * @param msg to format
+   * @return formatted message
+   */
+  private String formatLogMessage(String msg) {
+    // Use this to prepend to log messages.  Since they
+    // are long and usually wrap, without this it is
+    // difficult to tell where one stops and another starts
+    StringBuffer sb = new StringBuffer();
+    sb.append("\u2022");
+    sb.append(" ");
+    sb.append(msg);
+    return sb.toString();
+  }
+
+  /**
+   * Formats a time string into a human friendly format.
+   * @param timeString the is human hostile
+   * @return string of time that is human friendly
+   */
+  private Message formatTimeString(String timeString) {
+    Message ret = Message.EMPTY;
+    if (timeString != null && timeString.length() > 0) {
+      try {
+        SimpleDateFormat dateFormat;
+        if (timeString.endsWith("Z")) {
+          dateFormat = new SimpleDateFormat(DATE_FORMAT_GMT_TIME);
+          dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+        } else {
+          dateFormat = new SimpleDateFormat(DATE_FORMAT_COMPACT_LOCAL_TIME);
+        }
+        Date date = dateFormat.parse(timeString);
+        DateFormat df = DateFormat.getDateTimeInstance(
+                DateFormat.FULL,
+                DateFormat.FULL);
+        ret = Message.raw(df.format(date));
+      } catch (ParseException pe){
+        ret = Message.raw(timeString);
+      }
+    }
+    return ret;
+  }
+
+  private Task getTask() {
+    if (task == null && className != null) {
+      try {
+        Class<?> clazz = Class.forName(className);
+        Object o = clazz.newInstance();
+        if (Task.class.isAssignableFrom(o.getClass())) {
+          this.task = (Task) o;
+        }
+      } catch (Exception e) {
+        // ignore; this is best effort
+      }
+    }
+    return task;
+  }
+
+}
diff --git a/opends/src/server/org/opends/server/tools/tasks/TaskScheduleInformation.java b/opends/src/server/org/opends/server/tools/tasks/TaskScheduleInformation.java
index 2318bf5..77137f0 100644
--- a/opends/src/server/org/opends/server/tools/tasks/TaskScheduleInformation.java
+++ b/opends/src/server/org/opends/server/tools/tasks/TaskScheduleInformation.java
@@ -35,7 +35,7 @@
  * Interface for tools that are capable of scheduling a task remotely
  * through the task backend.
  *
- * @see TaskSchedulingClient
+ * @see TaskClient
  */
 public interface TaskScheduleInformation {
 
diff --git a/opends/src/server/org/opends/server/tools/tasks/TaskSchedulingClient.java b/opends/src/server/org/opends/server/tools/tasks/TaskSchedulingClient.java
deleted file mode 100644
index 90f700f..0000000
--- a/opends/src/server/org/opends/server/tools/tasks/TaskSchedulingClient.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at
- * trunk/opends/resource/legal-notices/OpenDS.LICENSE
- * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at
- * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
- * add the following below this CDDL HEADER, with the fields enclosed
- * by brackets "[]" replaced with your own identifying information:
- *      Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- *
- *
- *      Portions Copyright 2007 Sun Microsystems, Inc.
- */
-
-package org.opends.server.tools.tasks;
-
-import org.opends.server.types.LDAPException;
-import org.opends.server.types.RawAttribute;
-import org.opends.server.types.ResultCode;
-import org.opends.server.protocols.asn1.ASN1OctetString;
-import org.opends.server.protocols.asn1.ASN1Exception;
-import org.opends.server.protocols.ldap.LDAPControl;
-import org.opends.server.protocols.ldap.AddRequestProtocolOp;
-import org.opends.server.protocols.ldap.LDAPMessage;
-import org.opends.server.protocols.ldap.LDAPResultCode;
-import org.opends.server.protocols.ldap.LDAPConstants;
-import org.opends.server.protocols.ldap.ExtendedResponseProtocolOp;
-import org.opends.server.protocols.ldap.AddResponseProtocolOp;
-import org.opends.server.protocols.ldap.LDAPAttribute;
-import static org.opends.server.config.ConfigConstants.ATTR_TASK_ID;
-import static org.opends.server.config.ConfigConstants.SCHEDULED_TASK_BASE_RDN;
-import static org.opends.server.config.ConfigConstants.DN_TASK_ROOT;
-import static org.opends.server.config.ConfigConstants.ATTR_OBJECTCLASS;
-import static org.opends.server.config.ConfigConstants.ATTR_TASK_CLASS;
-import org.opends.server.config.ConfigConstants;
-import static org.opends.server.util.ServerConstants.MAX_LINE_WIDTH;
-import static org.opends.server.util.StaticUtils.wrapText;
-import org.opends.server.tools.LDAPConnection;
-import org.opends.server.tools.LDAPReader;
-import org.opends.server.tools.LDAPWriter;
-import org.opends.messages.Message;
-import static org.opends.messages.ToolMessages.*;
-
-import java.util.UUID;
-import java.util.ArrayList;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.io.IOException;
-import java.io.PrintStream;
-
-/**
- * Helper class for interacting with the task backend on behalf of utilities
- * that are capable of being scheduled.
- */
-public class TaskSchedulingClient {
-
-  /**
-   * Connection through which task scheduling will take place.
-   */
-  protected LDAPConnection connection;
-
-  /**
-   * Creates a new TaskClient for interacting with the task backend remotely.
-   * @param conn for accessing the task backend
-   */
-  public TaskSchedulingClient(LDAPConnection conn) {
-    this.connection = conn;
-  }
-
-  /**
-   * Schedule a task for execution by writing an entry to the task backend.
-   *
-   * @param information to be scheduled
-   * @param out stream for writing error messages; may be null
-   * @param err stream for writing error messages; may be null
-   * @return int representing an LDAP return code
-   */
-  public synchronized int schedule(TaskScheduleInformation information,
-                                   PrintStream out, PrintStream err) {
-
-    // Attempt to connect and authenticate to the Directory Server.
-    AtomicInteger nextMessageID = new AtomicInteger(1);
-
-    LDAPReader reader = connection.getLDAPReader();
-    LDAPWriter writer = connection.getLDAPWriter();
-
-    // Construct the add request to send to the server.
-    String taskID = UUID.randomUUID().toString();
-    ASN1OctetString entryDN =
-         new ASN1OctetString(ATTR_TASK_ID + "=" + taskID + "," +
-                             SCHEDULED_TASK_BASE_RDN + "," + DN_TASK_ROOT);
-
-    ArrayList<LDAPControl> controls = new ArrayList<LDAPControl>();
-
-    ArrayList<RawAttribute> attributes = new ArrayList<RawAttribute>();
-
-    ArrayList<ASN1OctetString> ocValues = new ArrayList<ASN1OctetString>(3);
-    ocValues.add(new ASN1OctetString("top"));
-    ocValues.add(new ASN1OctetString(ConfigConstants.OC_TASK));
-    ocValues.add(new ASN1OctetString(information.getTaskObjectclass()));
-    attributes.add(new LDAPAttribute(ATTR_OBJECTCLASS, ocValues));
-
-    ArrayList<ASN1OctetString> taskIDValues = new ArrayList<ASN1OctetString>(1);
-    taskIDValues.add(new ASN1OctetString(taskID));
-    attributes.add(new LDAPAttribute(ATTR_TASK_ID, taskIDValues));
-
-    ArrayList<ASN1OctetString> classValues = new ArrayList<ASN1OctetString>(1);
-    classValues.add(new ASN1OctetString(information.getTaskClass().getName()));
-    attributes.add(new LDAPAttribute(ATTR_TASK_CLASS, classValues));
-
-    information.addTaskAttributes(attributes);
-
-    AddRequestProtocolOp addRequest = new AddRequestProtocolOp(entryDN,
-                                                               attributes);
-    LDAPMessage requestMessage =
-         new LDAPMessage(nextMessageID.getAndIncrement(), addRequest, controls);
-
-    // Send the request to the server and read the response.
-    LDAPMessage responseMessage;
-    try
-    {
-      writer.writeMessage(requestMessage);
-
-      responseMessage = reader.readMessage();
-      if (responseMessage == null)
-      {
-        Message message = ERR_TASK_CLIENT_UNEXPECTED_CONNECTION_CLOSURE.get();
-        if (err != null) err.println(wrapText(message, MAX_LINE_WIDTH));
-        return LDAPResultCode.CLIENT_SIDE_SERVER_DOWN;
-      }
-    }
-    catch (IOException ioe)
-    {
-      Message message = ERR_TASK_CLIENT_IO_ERROR.get(String.valueOf(ioe));
-      if (err != null) err.println(wrapText(message, MAX_LINE_WIDTH));
-      return LDAPResultCode.CLIENT_SIDE_SERVER_DOWN;
-    }
-    catch (ASN1Exception ae)
-    {
-      Message message = ERR_TASK_CLIENT_DECODE_ERROR.get(ae.getMessage());
-      if (err != null) err.println(wrapText(message, MAX_LINE_WIDTH));
-      return LDAPResultCode.CLIENT_SIDE_DECODING_ERROR;
-    }
-    catch (LDAPException le)
-    {
-      Message message = ERR_TASK_CLIENT_DECODE_ERROR.get(le.getMessage());
-      if (err != null) err.println(wrapText(message, MAX_LINE_WIDTH));
-      return LDAPResultCode.CLIENT_SIDE_DECODING_ERROR;
-    }
-
-    if (responseMessage.getProtocolOpType() !=
-        LDAPConstants.OP_TYPE_ADD_RESPONSE)
-    {
-      if (responseMessage.getProtocolOpType() ==
-          LDAPConstants.OP_TYPE_EXTENDED_RESPONSE)
-      {
-        // It's possible that this is a notice of disconnection, which we can
-        // probably interpret as a "success" in this case.
-        ExtendedResponseProtocolOp extendedResponse =
-             responseMessage.getExtendedResponseProtocolOp();
-        String responseOID = extendedResponse.getOID();
-        if ((responseOID != null) &&
-            (responseOID.equals(LDAPConstants.OID_NOTICE_OF_DISCONNECTION)))
-        {
-          Message message = extendedResponse.getErrorMessage();
-          if (message != null)
-          {
-            if (err != null) err.println(wrapText(message, MAX_LINE_WIDTH));
-          }
-
-          return extendedResponse.getResultCode();
-        }
-      }
-
-      Message message = ERR_TASK_CLIENT_INVALID_RESPONSE_TYPE.get(
-              responseMessage.getProtocolOpName());
-      if (err != null) err.println(wrapText(message, MAX_LINE_WIDTH));
-      return LDAPResultCode.CLIENT_SIDE_LOCAL_ERROR;
-    }
-
-    AddResponseProtocolOp addResponse =
-         responseMessage.getAddResponseProtocolOp();
-    Message errorMessage = addResponse.getErrorMessage();
-    if (errorMessage != null && err != null)
-    {
-      err.println(wrapText(errorMessage, MAX_LINE_WIDTH));
-    }
-
-    if (addResponse.getResultCode() == ResultCode.SUCCESS.getIntValue() &&
-          out != null)
-    {
-      out.println(wrapText(INFO_TASK_CLIENT_TASK_SCHEDULED.get(taskID),
-                           MAX_LINE_WIDTH));
-    }
-
-    return addResponse.getResultCode();
-  }
-
-}
diff --git a/opends/src/server/org/opends/server/tools/tasks/TaskTool.java b/opends/src/server/org/opends/server/tools/tasks/TaskTool.java
index 8cd4aeb..3d6b3fa 100644
--- a/opends/src/server/org/opends/server/tools/tasks/TaskTool.java
+++ b/opends/src/server/org/opends/server/tools/tasks/TaskTool.java
@@ -31,13 +31,15 @@
 import org.opends.server.util.args.ArgumentException;
 import static org.opends.server.util.StaticUtils.wrapText;
 import static org.opends.server.util.ServerConstants.MAX_LINE_WIDTH;
-import org.opends.server.protocols.ldap.LDAPResultCode;
+import org.opends.server.protocols.asn1.ASN1Exception;
 import org.opends.server.tools.LDAPConnection;
 import org.opends.server.tools.LDAPConnectionException;
+import org.opends.server.types.LDAPException;
 import org.opends.messages.Message;
-import static org.opends.messages.ToolMessages.ERR_LDAP_CONN_CANNOT_CONNECT;
+import static org.opends.messages.ToolMessages.*;
 
 import java.io.PrintStream;
+import java.io.IOException;
 
 /**
  * Base class for tools that are capable of operating either by running
@@ -61,24 +63,38 @@
                         boolean initializeServer,
                         PrintStream out, PrintStream err) {
     int ret;
+    String taskId;
     if (argParser.isLdapOperation())
     {
       try {
         LDAPConnection conn = argParser.connect(out, err);
-        TaskSchedulingClient tc = new TaskSchedulingClient(conn);
-        ret = tc.schedule(this, out, err);
+        TaskClient tc = new TaskClient(conn);
+        taskId = tc.schedule(this);
+        out.println(wrapText(INFO_TASK_TOOL_TASK_SCHEDULED.get(taskId),
+                MAX_LINE_WIDTH));
+        ret = 0;
       } catch (LDAPConnectionException e) {
         Message message = ERR_LDAP_CONN_CANNOT_CONNECT.get(e.getMessage());
         if (err != null) err.println(wrapText(message, MAX_LINE_WIDTH));
-        ret = LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR;
+        ret = 1;
       } catch (ArgumentException e) {
         Message message = e.getMessageObject();
         if (err != null) err.println(wrapText(message, MAX_LINE_WIDTH));
-        ret = LDAPResultCode.CLIENT_SIDE_PARAM_ERROR;
+        ret = 1;
+      } catch (IOException ioe) {
+        Message message = ERR_TASK_TOOL_IO_ERROR.get(String.valueOf(ioe));
+        if (err != null) err.println(wrapText(message, MAX_LINE_WIDTH));
+        ret = 1;
+      } catch (ASN1Exception ae) {
+        Message message = ERR_TASK_TOOL_DECODE_ERROR.get(ae.getMessage());
+        if (err != null) err.println(wrapText(message, MAX_LINE_WIDTH));
+        ret = 1;
+      } catch (LDAPException le) {
+        Message message = ERR_TASK_TOOL_DECODE_ERROR.get(le.getMessage());
+        if (err != null) err.println(wrapText(message, MAX_LINE_WIDTH));
+        ret = 1;
       }
-    }
-    else
-    {
+    } else {
       ret = processLocal(initializeServer, out, err);
     }
     return ret;
diff --git a/opends/src/server/org/opends/server/util/StaticUtils.java b/opends/src/server/org/opends/server/util/StaticUtils.java
index 2976d40..adb34a6 100644
--- a/opends/src/server/org/opends/server/util/StaticUtils.java
+++ b/opends/src/server/org/opends/server/util/StaticUtils.java
@@ -29,6 +29,8 @@
 import static org.opends.messages.UtilityMessages.*;
 import static org.opends.server.loggers.debug.DebugLogger.*;
 import static org.opends.server.util.ServerConstants.*;
+import org.opends.server.util.args.ArgumentException;
+import org.opends.server.util.args.Argument;
 
 import java.io.BufferedReader;
 import java.io.File;
@@ -50,6 +52,7 @@
 import org.opends.messages.Message;
 import org.opends.messages.MessageBuilder;
 import org.opends.messages.MessageDescriptor;
+import org.opends.messages.ToolMessages;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.loggers.debug.DebugTracer;
 import org.opends.server.types.Attribute;
@@ -3771,79 +3774,65 @@
     String padding = pb.toString();
 
     StringBuilder   buffer        = new StringBuilder();
-    StringTokenizer lineTokenizer = new StringTokenizer(text, "\r\n", true);
-    while (lineTokenizer.hasMoreTokens())
-    {
-      String line = lineTokenizer.nextToken();
-      if (line.equals("\r") || line.equals("\n"))
+    if (text != null) {
+      StringTokenizer lineTokenizer = new StringTokenizer(text, "\r\n", true);
+      while (lineTokenizer.hasMoreTokens())
       {
-        // It's an end-of-line character, so append it as-is.
-        buffer.append(line);
-      }
-      else if (line.length() < width)
-      {
-        // The line fits in the specified width, so append it as-is.
-        buffer.append(padding);
-        buffer.append(line);
-      }
-      else
-      {
-        // The line doesn't fit in the specified width, so it needs to be
-        // wrapped.  Do so at space boundaries.
-        StringBuilder   lineBuffer    = new StringBuilder();
-        StringBuilder   delimBuffer   = new StringBuilder();
-        StringTokenizer wordTokenizer = new StringTokenizer(line, " ", true);
-        while (wordTokenizer.hasMoreTokens())
+        String line = lineTokenizer.nextToken();
+        if (line.equals("\r") || line.equals("\n"))
         {
-          String word = wordTokenizer.nextToken();
-          if (word.equals(" "))
+          // It's an end-of-line character, so append it as-is.
+          buffer.append(line);
+        }
+        else if (line.length() < width)
+        {
+          // The line fits in the specified width, so append it as-is.
+          buffer.append(padding);
+          buffer.append(line);
+        }
+        else
+        {
+          // The line doesn't fit in the specified width, so it needs to be
+          // wrapped.  Do so at space boundaries.
+          StringBuilder   lineBuffer    = new StringBuilder();
+          StringBuilder   delimBuffer   = new StringBuilder();
+          StringTokenizer wordTokenizer = new StringTokenizer(line, " ", true);
+          while (wordTokenizer.hasMoreTokens())
           {
-            // It's a space, so add it to the delim buffer only if the line
-            // buffer is not empty.
-            if (lineBuffer.length() > 0)
+            String word = wordTokenizer.nextToken();
+            if (word.equals(" "))
             {
-              delimBuffer.append(word);
-            }
-          }
-          else if (word.length() > width)
-          {
-            // This is a long word that can't be wrapped, so we'll just have to
-            // make do.
-            if (lineBuffer.length() > 0)
-            {
-              buffer.append(padding);
-              buffer.append(lineBuffer);
-              buffer.append(EOL);
-              lineBuffer = new StringBuilder();
-            }
-            buffer.append(padding);
-            buffer.append(word);
-
-            if (wordTokenizer.hasMoreTokens())
-            {
-              // The next token must be a space, so remove it.  If there are
-              // still more tokens after that, then append an EOL.
-              wordTokenizer.nextToken();
-              if (wordTokenizer.hasMoreTokens())
+              // It's a space, so add it to the delim buffer only if the line
+              // buffer is not empty.
+              if (lineBuffer.length() > 0)
               {
-                buffer.append(EOL);
+                delimBuffer.append(word);
               }
             }
+            else if (word.length() > width)
+            {
+              // This is a long word that can't be wrapped, so we'll just have
+              // to make do.
+              if (lineBuffer.length() > 0)
+              {
+                buffer.append(padding);
+                buffer.append(lineBuffer);
+                buffer.append(EOL);
+                lineBuffer = new StringBuilder();
+              }
+              buffer.append(padding);
+              buffer.append(word);
 
-            if (delimBuffer.length() > 0)
-            {
-              delimBuffer = new StringBuilder();
-            }
-          }
-          else
-          {
-            // It's not a space, so see if we can fit it on the curent line.
-            int newLineLength = lineBuffer.length() + delimBuffer.length() +
-                                word.length();
-            if (newLineLength < width)
-            {
-              // It does fit on the line, so add it.
-              lineBuffer.append(delimBuffer).append(word);
+              if (wordTokenizer.hasMoreTokens())
+              {
+                // The next token must be a space, so remove it.  If there are
+                // still more tokens after that, then append an EOL.
+                wordTokenizer.nextToken();
+                if (wordTokenizer.hasMoreTokens())
+                {
+                  buffer.append(EOL);
+                }
+              }
 
               if (delimBuffer.length() > 0)
               {
@@ -3852,30 +3841,45 @@
             }
             else
             {
-              // It doesn't fit on the line, so end the current line and start
-              // a new one.
-              buffer.append(padding);
-              buffer.append(lineBuffer);
-              buffer.append(EOL);
-
-              lineBuffer = new StringBuilder();
-              lineBuffer.append(word);
-
-              if (delimBuffer.length() > 0)
+              // It's not a space, so see if we can fit it on the curent line.
+              int newLineLength = lineBuffer.length() + delimBuffer.length() +
+                                  word.length();
+              if (newLineLength < width)
               {
-                delimBuffer = new StringBuilder();
+                // It does fit on the line, so add it.
+                lineBuffer.append(delimBuffer).append(word);
+
+                if (delimBuffer.length() > 0)
+                {
+                  delimBuffer = new StringBuilder();
+                }
+              }
+              else
+              {
+                // It doesn't fit on the line, so end the current line and start
+                // a new one.
+                buffer.append(padding);
+                buffer.append(lineBuffer);
+                buffer.append(EOL);
+
+                lineBuffer = new StringBuilder();
+                lineBuffer.append(word);
+
+                if (delimBuffer.length() > 0)
+                {
+                  delimBuffer = new StringBuilder();
+                }
               }
             }
           }
-        }
 
-        // If there's anything left in the line buffer, then add it to the
-        // final buffer.
-        buffer.append(padding);
-        buffer.append(lineBuffer);
+          // If there's anything left in the line buffer, then add it to the
+          // final buffer.
+          buffer.append(padding);
+          buffer.append(lineBuffer);
+        }
       }
     }
-
     return buffer.toString();
   }
 
@@ -3909,5 +3913,32 @@
       return exitCode;
     }
   }
+
+  /**
+   * Checks that no more that one of a set of arguments is present.  This
+   * utility should be used after argument parser has parsed a set of
+   * arguments.
+   *
+   * @param  args to test for the presence of more than one
+   * @throws ArgumentException if more than one of <code>args</code> is
+   *         present and containing an error message identifying the
+   *         arguments in violation
+   */
+  public static void checkOnlyOneArgPresent(Argument... args)
+    throws ArgumentException
+  {
+    if (args != null) {
+      for (Argument arg : args) {
+        for (Argument otherArg : args) {
+          if (arg != otherArg && arg.isPresent() && otherArg.isPresent()) {
+            throw new ArgumentException(
+                    ToolMessages.ERR_INCOMPATIBLE_ARGUMENTS.get(
+                            arg.getName(), otherArg.getName()));
+          }
+        }
+      }
+    }
+  }
+
 }
 
diff --git a/opends/src/server/org/opends/server/util/args/ArgumentParser.java b/opends/src/server/org/opends/server/util/args/ArgumentParser.java
index fd230c5..63ea8ab 100644
--- a/opends/src/server/org/opends/server/util/args/ArgumentParser.java
+++ b/opends/src/server/org/opends/server/util/args/ArgumentParser.java
@@ -1004,22 +1004,22 @@
     String scriptName = System.getProperty(PROPERTY_SCRIPT_NAME);
     if ((scriptName == null) || (scriptName.length() == 0))
     {
-      buffer.append("Usage:  java ");
+      buffer.append("Usage:  java "); // TODO: i18n
       buffer.append(mainClassName);
     }
     else
     {
-      buffer.append("Usage:  ");
+      buffer.append("Usage:  "); // TODO: i18n
       buffer.append(scriptName);
     }
 
-    buffer.append(" {options}");
+    buffer.append(" {options}"); // TODO: i18n
 
     if (allowsTrailingArguments)
     {
       if (trailingArgsDisplayName == null)
       {
-        buffer.append(" {trailing-arguments}");
+        buffer.append(" {trailing-arguments}"); // TODO: i18n
       }
       else
       {
@@ -1075,6 +1075,22 @@
 
 
   /**
+   * Retrieves a message containing usage information based on the defined
+   * arguments.
+   *
+   * @return  A string containing usage information based on the defined
+   *          arguments.
+   */
+  public Message getUsageMessage()
+  {
+    StringBuilder buffer = new StringBuilder();
+    getUsage(buffer);
+
+    // TODO: rework getUsage(OutputStream) to work with messages framework
+    return Message.raw(buffer.toString());
+  }
+
+  /**
    * Retrieves a string containing usage information based on the defined
    * arguments.
    *
diff --git a/opends/src/server/org/opends/server/util/args/LDAPConnectionArgumentParser.java b/opends/src/server/org/opends/server/util/args/LDAPConnectionArgumentParser.java
index 4444905..5482cc3 100644
--- a/opends/src/server/org/opends/server/util/args/LDAPConnectionArgumentParser.java
+++ b/opends/src/server/org/opends/server/util/args/LDAPConnectionArgumentParser.java
@@ -34,11 +34,13 @@
 import org.opends.server.tools.SSLConnectionFactory;
 import org.opends.server.tools.SSLConnectionException;
 import org.opends.server.tools.LDAPConnectionException;
-import static org.opends.server.tools.ToolConstants.*;
 import static org.opends.server.util.ServerConstants.MAX_LINE_WIDTH;
 import static org.opends.server.util.StaticUtils.wrapText;
+import org.opends.server.util.cli.LDAPConnectionConsoleInteraction;
+import org.opends.server.admin.client.cli.SecureConnectionCliArgs;
 
 import java.util.LinkedList;
+import java.util.LinkedHashSet;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.io.PrintStream;
 
@@ -48,53 +50,7 @@
  */
 public class LDAPConnectionArgumentParser extends ArgumentParser {
 
-  /** Argument indicating whether all SSL certs will be trusted. */
-  protected BooleanArgument   trustAll;
-
-  /** Argument indicating whether or not to use SSL. */
-  protected BooleanArgument   useSSL;
-
-  /** Argument indicating whether or not to use StartTLS. */
-  protected BooleanArgument   useStartTLS;
-
-  /** Argument indicating location of a bind password file. */
-  protected FileBasedArgument bindPWFile;
-
-  /** Argument indicating the location of the keystore password file. */
-  protected FileBasedArgument keyStorePWFile;
-
-  /** Argument indicating the location of the trust store password file. */
-  protected FileBasedArgument trustStorePWFile;
-
-  /** Argument indicating the port of the directory server. */
-  protected IntegerArgument   port;
-
-  /** Argument indicating the DN of the user with which to bind. */
-  protected StringArgument    bindDN;
-
-  /** Argument indicating the password of the user with which to bind. */
-  protected StringArgument    bindPW;
-
-  /** Argument indicating the nickname of the certificate to use. */
-  protected StringArgument    certNickname;
-
-  /** Argument indicating the hostname of the directory server. */
-  protected StringArgument    host;
-
-  /** Argument indicating the location of the keystore file. */
-  protected StringArgument    keyStoreFile;
-
-  /** Argument indicating the password fo the keystore. */
-  protected StringArgument    keyStorePW;
-
-  /** Argument indicating a SASL option. */
-  protected StringArgument    saslOption;
-
-  /** Argument indicating the location of the trust store file. */
-  protected StringArgument    trustStoreFile;
-
-  /** Argument indicating the password of the trust store. */
-  protected StringArgument    trustStorePW;
+  private SecureConnectionCliArgs args;
 
   /**
    * Creates a new instance of this argument parser with no arguments.
@@ -169,7 +125,9 @@
    *         false otherwise
    */
   public boolean isLdapOperation() {
-    return host.isPresent() || port.isPresent() || bindDN.isPresent();
+    return args.hostNameArg.isPresent() ||
+            args.portArg.isPresent() ||
+            args.bindDnArg.isPresent();
   }
 
   /**
@@ -188,13 +146,36 @@
   public LDAPConnection connect(PrintStream out, PrintStream err)
           throws LDAPConnectionException, ArgumentException
   {
+    return connect(this.args, out, err);
+  }
+
+
+  /**
+   * Creates a new LDAPConnection and invokes a connect operation using
+   * information provided in the parsed set of arguments that were provided
+   * by the user.
+   *
+   * @param args with which to connect
+   * @param out stream to write messages
+   * @param err stream to write messages
+   * @return LDAPConnection created by this class from parsed arguments
+   * @throws LDAPConnectionException if there was a problem connecting
+   *         to the server indicated by the input arguments
+   * @throws ArgumentException if there was a problem processing the input
+   *         arguments
+   */
+  private LDAPConnection connect(SecureConnectionCliArgs args,
+                                PrintStream out, PrintStream err)
+          throws LDAPConnectionException, ArgumentException
+  {
     // If both a bind password and bind password file were provided, then return
     // an error.
-    if (bindPW.isPresent() && bindPWFile.isPresent())
+    if (args.bindPasswordArg.isPresent() &&
+            args.bindPasswordFileArg.isPresent())
     {
       Message message = ERR_LDAP_CONN_MUTUALLY_EXCLUSIVE_ARGUMENTS.get(
-              bindPW.getLongIdentifier(),
-              bindPWFile.getLongIdentifier());
+              args.bindPasswordArg.getLongIdentifier(),
+              args.bindPasswordFileArg.getLongIdentifier());
       err.println(wrapText(message, MAX_LINE_WIDTH));
       throw new ArgumentException(message);
     }
@@ -202,22 +183,24 @@
 
     // If both a key store password and key store password file were provided,
     // then return an error.
-    if (keyStorePW.isPresent() && keyStorePWFile.isPresent())
+    if (args.keyStorePasswordArg.isPresent() &&
+            args.keyStorePasswordFileArg.isPresent())
     {
       Message message = ERR_LDAP_CONN_MUTUALLY_EXCLUSIVE_ARGUMENTS.get(
-              keyStorePW.getLongIdentifier(),
-              keyStorePWFile.getLongIdentifier());
+              args.keyStorePasswordArg.getLongIdentifier(),
+              args.keyStorePasswordFileArg.getLongIdentifier());
       throw new ArgumentException(message);
     }
 
 
     // If both a trust store password and trust store password file were
     // provided, then return an error.
-    if (trustStorePW.isPresent() && trustStorePWFile.isPresent())
+    if (args.trustStorePasswordArg.isPresent() &&
+            args.trustStorePasswordFileArg.isPresent())
     {
       Message message = ERR_LDAP_CONN_MUTUALLY_EXCLUSIVE_ARGUMENTS.get(
-              trustStorePW.getLongIdentifier(),
-              trustStorePWFile.getLongIdentifier());
+              args.trustStorePasswordArg.getLongIdentifier(),
+              args.trustStorePasswordFileArg.getLongIdentifier());
       err.println(wrapText(message, MAX_LINE_WIDTH));
       throw new ArgumentException(message);
     }
@@ -231,13 +214,13 @@
 
     // See if we should use SSL or StartTLS when establishing the connection.
     // If so, then make sure only one of them was specified.
-    if (useSSL.isPresent())
+    if (args.useSSLArg.isPresent())
     {
-      if (useStartTLS.isPresent())
+      if (args.useStartTLSArg.isPresent())
       {
         Message message = ERR_LDAP_CONN_MUTUALLY_EXCLUSIVE_ARGUMENTS.get(
-                useSSL.getLongIdentifier(),
-                useStartTLS.getLongIdentifier());
+                args.useSSLArg.getLongIdentifier(),
+                args.useSSLArg.getLongIdentifier());
         err.println(wrapText(message, MAX_LINE_WIDTH));
         throw new ArgumentException(message);
       }
@@ -246,7 +229,7 @@
         connectionOptions.setUseSSL(true);
       }
     }
-    else if (useStartTLS.isPresent())
+    else if (args.useStartTLSArg.isPresent())
     {
       connectionOptions.setStartTLS(true);
     }
@@ -254,14 +237,14 @@
 
     // If we should blindly trust any certificate, then install the appropriate
     // SSL connection factory.
-    if (useSSL.isPresent() || useStartTLS.isPresent())
+    if (args.useSSLArg.isPresent() || args.useStartTLSArg.isPresent())
     {
       try
       {
         String clientAlias;
-        if (certNickname.isPresent())
+        if (args.certNicknameArg.isPresent())
         {
-          clientAlias = certNickname.getValue();
+          clientAlias = args.certNicknameArg.getValue();
         }
         else
         {
@@ -269,10 +252,12 @@
         }
 
         SSLConnectionFactory sslConnectionFactory = new SSLConnectionFactory();
-        sslConnectionFactory.init(trustAll.isPresent(), keyStoreFile.getValue(),
-                                  keyStorePW.getValue(), clientAlias,
-                                  trustStoreFile.getValue(),
-                                  trustStorePW.getValue());
+        sslConnectionFactory.init(args.trustAllArg.isPresent(),
+                args.keyStorePathArg.getValue(),
+                args.keyStorePasswordArg.getValue(),
+                clientAlias,
+                args.trustStorePathArg.getValue(),
+                args.trustStorePasswordArg.getValue());
 
         connectionOptions.setSSLConnectionFactory(sslConnectionFactory);
       }
@@ -287,12 +272,12 @@
 
     // If one or more SASL options were provided, then make sure that one of
     // them was "mech" and specified a valid SASL mechanism.
-    if (saslOption.isPresent())
+    if (args.saslOptionArg.isPresent())
     {
       String             mechanism = null;
       LinkedList<String> options   = new LinkedList<String>();
 
-      for (String s : saslOption.getValues())
+      for (String s : args.saslOptionArg.getValues())
       {
         int equalPos = s.indexOf('=');
         if (equalPos <= 0)
@@ -330,155 +315,100 @@
         connectionOptions.addSASLProperty(option);
       }
     }
+    return connect(
+            args.hostNameArg.getValue(),
+            args.portArg.getIntValue(),
+            args.bindDnArg.getValue(),
+            args.bindPasswordArg.getValue(),
+            connectionOptions, out, err);
+  }
 
+  /**
+   * Creates a connection using a console interaction that will be used
+   * to potientially interact with the user to prompt for necessary
+   * information for establishing the connection.
+   *
+   * @param ui user interaction for prompting the user
+   * @param out stream to write messages
+   * @param err stream to write messages
+   * @return LDAPConnection created by this class from parsed arguments
+   * @throws LDAPConnectionException if there was a problem connecting
+   *         to the server indicated by the input arguments
+   */
+  public LDAPConnection connect(LDAPConnectionConsoleInteraction ui,
+                                PrintStream out, PrintStream err)
+          throws LDAPConnectionException
+  {
+    LDAPConnection connection = null;
+    try {
+      ui.run();
+      LDAPConnectionOptions options = new LDAPConnectionOptions();
+      options.setVersionNumber(3);
+      connection = connect(
+              ui.getHostName(),
+              ui.getPortNumber(),
+              ui.getBindDN(),
+              ui.getBindPassword(),
+              ui.populateLDAPOptions(options), out, err);
+    } catch (ArgumentException ae) {
+      err.println(ae.getMessageObject());
+    }
+    return connection;
+  }
+
+
+  /**
+   * Creates a connection from information provided.
+   *
+   * @param host of the server
+   * @param port of the server
+   * @param bindDN with which to connect
+   * @param bindPw with which to connect
+   * @param options with which to connect
+   * @param out stream to write messages
+   * @param err stream to write messages
+   * @return LDAPConnection created by this class from parsed arguments
+   * @throws LDAPConnectionException if there was a problem connecting
+   *         to the server indicated by the input arguments
+   */
+  public LDAPConnection connect(String host, int port,
+                                String bindDN, String bindPw,
+                                LDAPConnectionOptions options,
+                                PrintStream out,
+                                PrintStream err)
+          throws LDAPConnectionException
+  {
 
     // Attempt to connect and authenticate to the Directory Server.
     AtomicInteger nextMessageID = new AtomicInteger(1);
 
     LDAPConnection connection = new LDAPConnection(
-            host.getValue(), port.getIntValue(),
-            connectionOptions, out, err);
+            host, port, options, out, err);
 
-    connection.connectToHost(bindDN.getValue(), bindPW.getValue(),
-                             nextMessageID);
+    connection.connectToHost(bindDN, bindPw, nextMessageID);
 
     return connection;
   }
 
+  /**
+   * Gets the arguments associated with this parser.
+   *
+   * @return arguments for this parser.
+   */
+  public SecureConnectionCliArgs getArguments() {
+    return args;
+  }
+
   private void addLdapConnectionArguments() {
-
-    try
-    {
-      host = new StringArgument(
-              "host", OPTION_SHORT_HOST,
-              OPTION_LONG_HOST, false, false, true,
-              OPTION_VALUE_HOST, "127.0.0.1", null,
-              INFO_LDAP_CONN_DESCRIPTION_HOST.get());
-      addArgument(host);
-
-      port = new IntegerArgument(
-              "port", OPTION_SHORT_PORT,
-              OPTION_LONG_PORT, false, false, true,
-              OPTION_VALUE_PORT, 389, null, true, 1,
-              true, 65535, INFO_LDAP_CONN_DESCRIPTION_PORT.get());
-      addArgument(port);
-
-      useSSL = new BooleanArgument(
-              "usessl", OPTION_SHORT_USE_SSL,
-              OPTION_LONG_USE_SSL,
-              INFO_LDAP_CONN_DESCRIPTION_USESSL.get());
-      addArgument(useSSL);
-
-      useStartTLS = new BooleanArgument(
-              "usestarttls", OPTION_SHORT_START_TLS,
-              OPTION_LONG_START_TLS,
-              INFO_LDAP_CONN_DESCRIPTION_USESTARTTLS.get());
-      addArgument(useStartTLS);
-
-      bindDN = new StringArgument(
-              "binddn", OPTION_SHORT_BINDDN,
-              OPTION_LONG_BINDDN, false, false, true,
-              OPTION_VALUE_BINDDN, null, null,
-              INFO_LDAP_CONN_DESCRIPTION_BINDDN.get());
-      addArgument(bindDN);
-
-      bindPW = new StringArgument(
-              "bindpw", OPTION_SHORT_BINDPWD,
-              OPTION_LONG_BINDPWD, false, false,
-              true,
-              OPTION_VALUE_BINDPWD, null, null,
-              INFO_LDAP_CONN_DESCRIPTION_BINDPW.get());
-      addArgument(bindPW);
-
-      bindPWFile = new FileBasedArgument(
-              "bindpwfile",
-              OPTION_SHORT_BINDPWD_FILE,
-              OPTION_LONG_BINDPWD_FILE,
-              false, false,
-              OPTION_VALUE_BINDPWD_FILE,
-              null, null,
-              INFO_LDAP_CONN_DESCRIPTION_BINDPWFILE.get());
-      addArgument(bindPWFile);
-
-      saslOption = new StringArgument(
-              "sasloption", OPTION_SHORT_SASLOPTION,
-              OPTION_LONG_SASLOPTION, false,
-              true, true,
-              OPTION_VALUE_SASLOPTION, null, null,
-              INFO_LDAP_CONN_DESCRIPTION_SASLOPTIONS.get());
-      addArgument(saslOption);
-
-      trustAll = new BooleanArgument(
-              "trustall", 'X', "trustAll",
-              INFO_LDAP_CONN_DESCRIPTION_TRUST_ALL.get());
-      addArgument(trustAll);
-
-      keyStoreFile = new StringArgument(
-              "keystorefile",
-              OPTION_SHORT_KEYSTOREPATH,
-              OPTION_LONG_KEYSTOREPATH,
-              false, false, true,
-              OPTION_VALUE_KEYSTOREPATH,
-              null, null,
-              INFO_LDAP_CONN_DESCRIPTION_KSFILE.get());
-      addArgument(keyStoreFile);
-
-      keyStorePW = new StringArgument(
-              "keystorepw", OPTION_SHORT_KEYSTORE_PWD,
-              OPTION_LONG_KEYSTORE_PWD,
-              false, false, true,
-              OPTION_VALUE_KEYSTORE_PWD,
-              null, null,
-              INFO_LDAP_CONN_DESCRIPTION_KSPW.get());
-      addArgument(keyStorePW);
-
-      keyStorePWFile = new FileBasedArgument(
-              "keystorepwfile",
-              OPTION_SHORT_KEYSTORE_PWD_FILE,
-              OPTION_LONG_KEYSTORE_PWD_FILE,
-              false, false,
-              OPTION_VALUE_KEYSTORE_PWD_FILE,
-              null, null,
-              INFO_LDAP_CONN_DESCRIPTION_KSPWFILE.get());
-      addArgument(keyStorePWFile);
-
-      certNickname = new StringArgument(
-              "certnickname", 'N', "certNickname",
-              false, false, true, "{nickname}", null,
-              null, INFO_DESCRIPTION_CERT_NICKNAME.get());
-      addArgument(certNickname);
-
-      trustStoreFile = new StringArgument(
-              "truststorefile",
-              OPTION_SHORT_TRUSTSTOREPATH,
-              OPTION_LONG_TRUSTSTOREPATH,
-              false, false, true,
-              OPTION_VALUE_TRUSTSTOREPATH,
-              null, null,
-              INFO_LDAP_CONN_DESCRIPTION_TSFILE.get());
-      addArgument(trustStoreFile);
-
-      trustStorePW = new StringArgument(
-              "truststorepw", 'T',
-              OPTION_LONG_TRUSTSTORE_PWD,
-              false, false,
-              true, OPTION_VALUE_TRUSTSTORE_PWD, null,
-              null, INFO_LDAP_CONN_DESCRIPTION_TSPW.get());
-      addArgument(trustStorePW);
-
-      trustStorePWFile = new FileBasedArgument(
-              "truststorepwfile",
-              OPTION_SHORT_TRUSTSTORE_PWD_FILE,
-              OPTION_LONG_TRUSTSTORE_PWD_FILE,
-              false, false,
-              OPTION_VALUE_TRUSTSTORE_PWD_FILE, null, null,
-              INFO_LDAP_CONN_DESCRIPTION_TSPWFILE.get());
-      addArgument(trustStorePWFile);
-
+    args = new SecureConnectionCliArgs();
+    try {
+      LinkedHashSet<Argument> argSet = args.createGlobalArguments();
+      for (Argument arg : argSet) {
+        addArgument(arg);
+      }
     }
-    catch (ArgumentException ae)
-    {
-      // Should never happen
+    catch (ArgumentException ae) {
+      ae.printStackTrace(); // Should never happen
     }
 
   }
diff --git a/opends/src/server/org/opends/server/util/cli/ConsoleApplication.java b/opends/src/server/org/opends/server/util/cli/ConsoleApplication.java
index 0033045..4b9b23c 100644
--- a/opends/src/server/org/opends/server/util/cli/ConsoleApplication.java
+++ b/opends/src/server/org/opends/server/util/cli/ConsoleApplication.java
@@ -352,6 +352,16 @@
   }
 
 
+  /**
+   * Displays a message to the error stream.
+   *
+   * @param msg
+   *          The message.
+   */
+  public final void print(Message msg) {
+    err.print(wrapText(msg, MAX_LINE_WIDTH));
+  }
+
 
   /**
    * Displays a message to the error stream indented by the specified
diff --git a/opends/src/server/org/opends/server/util/cli/LDAPConnectionConsoleInteraction.java b/opends/src/server/org/opends/server/util/cli/LDAPConnectionConsoleInteraction.java
index ce9bce7..993ddbd 100644
--- a/opends/src/server/org/opends/server/util/cli/LDAPConnectionConsoleInteraction.java
+++ b/opends/src/server/org/opends/server/util/cli/LDAPConnectionConsoleInteraction.java
@@ -31,6 +31,7 @@
 import static org.opends.messages.UtilityMessages.*;
 import static org.opends.messages.ToolMessages.INFO_LDAPAUTH_PASSWORD_PROMPT;
 import org.opends.server.tools.dsconfig.ArgumentExceptionFactory;
+import org.opends.server.tools.LDAPConnectionOptions;
 import org.opends.server.admin.client.cli.SecureConnectionCliArgs;
 import org.opends.server.util.args.ArgumentException;
 import org.opends.server.util.SelectableCertificateKeyManager;
@@ -1316,4 +1317,23 @@
     }
   }
 
+ /**
+  * Populates an a set of LDAP options with state from this interaction.
+  *
+  * @param  options existing set of options; may be null in which case this
+  *         method will create a new set of <code>LDAPConnectionOptions</code>
+  *         to be returned
+  * @return used during this interaction
+  */
+ public LDAPConnectionOptions populateLDAPOptions(
+         LDAPConnectionOptions options)
+ {
+   if (options == null) {
+     options = new LDAPConnectionOptions();
+   }
+   options.setUseSSL(this.useSSL);
+   options.setStartTLS(this.useStartTLS);
+   return options;
+ }
+
 }
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/tasks/DummyTask.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/tasks/DummyTask.java
index 2442335..6f8b98b 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/tasks/DummyTask.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/tasks/DummyTask.java
@@ -54,7 +54,12 @@
   // the task gets interrupted.
   private volatile TaskState interruptedState;
 
-
+  /**
+   * {@inheritDoc}
+   */
+  public Message getDisplayName() {
+    return Message.raw("Dummy");
+  }
 
   /**
    * {@inheritDoc}

--
Gitblit v1.10.0