mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

kenneth_suter
13.14.2007 72c939b50428a2aa1f924a04ecdedee93e1383f4
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.

When starting the tool, 'manage-tasks' requires a connection to the directory to interact with the backend and prompts for information (unless given the -n/--no-prompt option) like 'dsconfig'
1 files deleted
6 files added
28 files modified
3382 ■■■■ changed files
opends/resource/bin/manage-tasks 37 ●●●●● patch | view | raw | blame | history
opends/resource/bin/manage-tasks.bat 33 ●●●●● patch | view | raw | blame | history
opends/src/messages/messages/task.properties 70 ●●●●● patch | view | raw | blame | history
opends/src/messages/messages/tools.properties 66 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/client/cli/SecureConnectionCliArgs.java 14 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/task/FailedDependencyAction.java 23 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/task/Task.java 42 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/task/TaskScheduler.java 11 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/task/TaskState.java 40 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tasks/AddSchemaFileTask.java 7 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tasks/BackupTask.java 62 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tasks/DisconnectClientTask.java 7 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tasks/EnterLockdownModeTask.java 8 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tasks/ExportTask.java 74 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tasks/ImportTask.java 90 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tasks/InitializeTargetTask.java 8 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tasks/InitializeTask.java 7 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tasks/LeaveLockdownModeTask.java 8 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tasks/RebuildTask.java 8 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tasks/RestoreTask.java 34 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tasks/SetGenerationIdTask.java 8 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tasks/ShutdownTask.java 6 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/ManageTasks.java 926 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/tasks/TaskClient.java 378 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/tasks/TaskClientException.java 84 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/tasks/TaskEntry.java 482 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/tasks/TaskScheduleInformation.java 2 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/tasks/TaskSchedulingClient.java 211 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/tasks/TaskTool.java 34 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/util/StaticUtils.java 195 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/util/args/ArgumentParser.java 24 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/util/args/LDAPConnectionArgumentParser.java 346 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/util/cli/ConsoleApplication.java 10 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/util/cli/LDAPConnectionConsoleInteraction.java 20 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/tasks/DummyTask.java 7 ●●●● patch | view | raw | blame | history
opends/resource/bin/manage-tasks
New file
@@ -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" "${@}"
opends/resource/bin/manage-tasks.bat
New file
@@ -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" %*
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
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
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);
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;
  }
}
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;
  }
}
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;
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;
  }
}
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}
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
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}
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}
   */
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}
   */
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}
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?
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()))
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}
   */
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
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
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()))
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
opends/src/server/org/opends/server/tools/ManageTasks.java
New file
@@ -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();
      }
    }
  }
}
opends/src/server/org/opends/server/tools/tasks/TaskClient.java
New file
@@ -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);
  }
}
opends/src/server/org/opends/server/tools/tasks/TaskClientException.java
New file
@@ -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);
  }
}
opends/src/server/org/opends/server/tools/tasks/TaskEntry.java
New file
@@ -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;
  }
}
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 {
opends/src/server/org/opends/server/tools/tasks/TaskSchedulingClient.java
File was deleted
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;
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()));
          }
        }
      }
    }
  }
}
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.
   *
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
    }
  }
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
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;
 }
}
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}