opends/resource/bin/_client-script.bat
@@ -23,7 +23,7 @@ rem CDDL HEADER END rem rem rem Copyright 2006-2009 Sun Microsystems, Inc. rem Copyright 2006-2010 Sun Microsystems, Inc. rem This script is used to invoke various client-side processes. It should not rem be invoked directly by end users. @@ -55,7 +55,7 @@ call "%INSTALL_ROOT%\lib\_script-util.bat" %* if NOT %errorlevel% == 0 exit /B %errorlevel% "%OPENDS_JAVA_BIN%" %OPENDS_JAVA_ARGS% %SCRIPT_NAME_ARG% %OPENDS_INVOKE_CLASS% %* "%OPENDS_JAVA_BIN%" %OPENDS_JAVA_ARGS% %SCRIPT_ARGS% %SCRIPT_NAME_ARG% %OPENDS_INVOKE_CLASS% %* :end opends/resource/bin/_client-script.sh
@@ -23,7 +23,7 @@ # CDDL HEADER END # # # Copyright 2006-2008 Sun Microsystems, Inc. # Copyright 2006-2010 Sun Microsystems, Inc. # This script is used to invoke various client-side processes. It should not @@ -66,4 +66,4 @@ fi # Launch the appropriate client utility. "${OPENDS_JAVA_BIN}" ${OPENDS_JAVA_ARGS} ${SCRIPT_NAME_ARG} "${OPENDS_INVOKE_CLASS}" "${@}" "${OPENDS_JAVA_BIN}" ${OPENDS_JAVA_ARGS} ${SCRIPT_ARGS} ${SCRIPT_NAME_ARG} "${OPENDS_INVOKE_CLASS}" "${@}" opends/resource/bin/_mixed-script.bat
@@ -23,7 +23,7 @@ rem CDDL HEADER END rem rem rem Copyright 2006-2009 Sun Microsystems, Inc. rem Copyright 2006-2010 Sun Microsystems, Inc. rem This script is used to invoke various server-side processes. It should not rem be invoked directly by end users. @@ -90,7 +90,7 @@ if NOT %errorlevel% == 0 exit /B %errorlevel% set SCRIPT_NAME_ARG="-Dorg.opends.server.scriptName=%OLD_SCRIPT_NAME%" "%OPENDS_JAVA_BIN%" %OPENDS_JAVA_ARGS% %SCRIPT_NAME_ARG% %OPENDS_INVOKE_CLASS% --configClass org.opends.server.extensions.ConfigFileHandler --configFile "%INSTANCE_ROOT%\config\config.ldif" %* "%OPENDS_JAVA_BIN%" %OPENDS_JAVA_ARGS% %SCRIPT_ARGS% %SCRIPT_NAME_ARG% %OPENDS_INVOKE_CLASS% --configClass org.opends.server.extensions.ConfigFileHandler --configFile "%INSTANCE_ROOT%\config\config.ldif" %* goto end opends/resource/bin/_mixed-script.sh
@@ -23,7 +23,7 @@ # CDDL HEADER END # # # Copyright 2008 Sun Microsystems, Inc. # Copyright 2008-2010 Sun Microsystems, Inc. # This script is used to invoke processes that might be run on server or @@ -78,7 +78,7 @@ export SCRIPT_NAME_ARG # Check whether is local or remote "${OPENDS_JAVA_BIN}" ${OPENDS_JAVA_ARGS} ${SCRIPT_NAME_ARG} "${OPENDS_INVOKE_CLASS}" \ "${OPENDS_JAVA_BIN}" ${OPENDS_JAVA_ARGS} ${SCRIPT_ARGS} ${SCRIPT_NAME_ARG} "${OPENDS_INVOKE_CLASS}" \ --configClass org.opends.server.extensions.ConfigFileHandler \ --configFile "${INSTANCE_ROOT}/config/config.ldif" --testIfOffline "${@}" EC=${?} @@ -119,7 +119,7 @@ export SCRIPT_NAME_ARG # Launch the server utility. "${OPENDS_JAVA_BIN}" ${OPENDS_JAVA_ARGS} ${SCRIPT_NAME_ARG} "${OPENDS_INVOKE_CLASS}" \ "${OPENDS_JAVA_BIN}" ${OPENDS_JAVA_ARGS} ${SCRIPT_ARGS} ${SCRIPT_NAME_ARG} "${OPENDS_INVOKE_CLASS}" \ --configClass org.opends.server.extensions.ConfigFileHandler \ --configFile "${INSTANCE_ROOT}/config/config.ldif" "${@}" fi opends/resource/bin/_server-script.bat
@@ -23,7 +23,7 @@ rem CDDL HEADER END rem rem rem Copyright 2006-2009 Sun Microsystems, Inc. rem Copyright 2006-2010 Sun Microsystems, Inc. rem This script is used to invoke various server-side processes. It should not rem be invoked directly by end users. @@ -55,7 +55,7 @@ call "%INSTALL_ROOT%\lib\_script-util.bat" %* if NOT %errorlevel% == 0 exit /B %errorlevel% "%OPENDS_JAVA_BIN%" %OPENDS_JAVA_ARGS% %SCRIPT_NAME_ARG% %OPENDS_INVOKE_CLASS% --configClass org.opends.server.extensions.ConfigFileHandler --configFile "%INSTANCE_ROOT%\config\config.ldif" %* "%OPENDS_JAVA_BIN%" %OPENDS_JAVA_ARGS% %SCRIPT_ARGS% %SCRIPT_NAME_ARG% %OPENDS_INVOKE_CLASS% --configClass org.opends.server.extensions.ConfigFileHandler --configFile "%INSTANCE_ROOT%\config\config.ldif" %* :end opends/resource/bin/_server-script.sh
@@ -23,7 +23,7 @@ # CDDL HEADER END # # # Copyright 2006-2008 Sun Microsystems, Inc. # Copyright 2006-2010 Sun Microsystems, Inc. # This script is used to invoke various server-side processes. It should not @@ -60,6 +60,6 @@ fi # Launch the appropriate server utility. "${OPENDS_JAVA_BIN}" ${OPENDS_JAVA_ARGS} ${SCRIPT_NAME_ARG} "${OPENDS_INVOKE_CLASS}" \ "${OPENDS_JAVA_BIN}" ${OPENDS_JAVA_ARGS} ${SCRIPT_ARGS} ${SCRIPT_NAME_ARG} "${OPENDS_INVOKE_CLASS}" \ --configClass org.opends.server.extensions.ConfigFileHandler \ --configFile "${INSTANCE_ROOT}/config/config.ldif" "${@}" opends/resource/bin/dsreplication
@@ -23,16 +23,27 @@ # CDDL HEADER END # # # Copyright 2008 Sun Microsystems, Inc. # Copyright 2008-2010 Sun Microsystems, Inc. # This script may be used to perform some replication specific operations. OPENDS_INVOKE_CLASS="org.opends.server.tools.dsreplication.ReplicationCliMain" export OPENDS_INVOKE_CLASS SCRIPT_NAME="dsreplication" export SCRIPT_NAME SCRIPT_DIR=`dirname "${0}"` "${SCRIPT_DIR}/../lib/_client-script.sh" "${@}" if test "${RECURSIVE_LOCAL_CALL}" = "true" then SCRIPT_ARGS="" export SCRIPT_ARGS SCRIPT_NAME="dsreplication.offline" export SCRIPT_NAME else SCRIPT_ARGS="-Dorg.opends.server.dsreplicationcallstatus=firstcall" export SCRIPT_ARGS SCRIPT_NAME="dsreplication" export SCRIPT_NAME fi "${SCRIPT_DIR}/../lib/_server-script.sh" "${@}" opends/resource/bin/dsreplication.bat
@@ -23,10 +23,23 @@ rem CDDL HEADER END rem rem rem Copyright 2008 Sun Microsystems, Inc. rem Copyright 2008-2010 Sun Microsystems, Inc. setlocal set OPENDS_INVOKE_CLASS="org.opends.server.tools.dsreplication.ReplicationCliMain" if "%RECURSIVE_LOCAL_CALL%" == "true" goto callOffline goto callOnline :callOffline set SCRIPT_ARGS= set SCRIPT_NAME=dsreplication.offline goto callScript :callOnline set SCRIPT_ARGS=-Dorg.opends.server.dsreplicationcallstatus=firstcall set SCRIPT_NAME=dsreplication for %%i in (%~sf0) do call "%%~dPsi\..\lib\_client-script.bat" %* goto callScript :callScript for %%i in (%~sf0) do call "%%~dPsi\..\lib\_server-script.bat" %* opends/src/guitools/org/opends/guitools/controlpanel/util/ConfigFromDirContext.java
@@ -96,7 +96,7 @@ private boolean isLocal = true; private Map<String, CustomSearchResult> hmConnectionHandlersMonitor = private final Map<String, CustomSearchResult> hmConnectionHandlersMonitor = new HashMap<String, CustomSearchResult>(); /** @@ -784,11 +784,13 @@ * @param sr the search result. * @param searchBaseDN the base search. * @param taskEntries the collection of TaskEntries to be updated. * @param ex the list of exceptions to be updated if an error occurs. * @throws NamingException if there is an error retrieving the values of the * search result. */ protected void handleTaskSearchResult(SearchResult sr, String searchBaseDN, Collection<TaskEntry> taskEntries) private void handleTaskSearchResult(SearchResult sr, String searchBaseDN, Collection<TaskEntry> taskEntries, List<OpenDsException> ex) throws NamingException { CustomSearchResult csr = new CustomSearchResult(sr, searchBaseDN); @@ -801,7 +803,7 @@ } catch (OpenDsException ode) { exceptions.add(ode); ex.add(ode); } } @@ -847,7 +849,15 @@ } } private void updateTaskInformation(InitialLdapContext ctx, /** * Updates the provided list of TaskEntry with the task entries found in * a server. * @param ctx the connection to the server. * @param ex the list of exceptions encountered while retrieving the task * entries. * @param ts the list of task entries to be updated. */ public void updateTaskInformation(InitialLdapContext ctx, List<OpenDsException> ex, Collection<TaskEntry> ts) { // Read monitoring information: since it is computed, it is faster @@ -870,7 +880,7 @@ while (taskEntries.hasMore()) { SearchResult sr = taskEntries.next(); handleTaskSearchResult(sr, ConfigConstants.DN_TASK_ROOT, ts); handleTaskSearchResult(sr, ConfigConstants.DN_TASK_ROOT, ts, ex); } } finally @@ -1052,7 +1062,8 @@ return isConnectionHandler; } private boolean isTaskEntry(CustomSearchResult csr) throws OpenDsException private static boolean isTaskEntry(CustomSearchResult csr) throws OpenDsException { boolean isTaskEntry = false; List<Object> vs = csr.getAttributeValues("objectclass"); opends/src/guitools/org/opends/guitools/controlpanel/util/ConfigFromFile.java
@@ -22,7 +22,7 @@ * CDDL HEADER END * * * Copyright 2008-2009 Sun Microsystems, Inc. * Copyright 2008-2010 Sun Microsystems, Inc. */ package org.opends.guitools.controlpanel.util; @@ -331,6 +331,7 @@ if (baseDN.getDn().equals(dn)) { baseDN.setType(BaseDNDescriptor.Type.REPLICATED); baseDN.setReplicaID(domain.getServerId()); } } } opends/src/messages/messages/admin_tool.properties
@@ -492,7 +492,7 @@ INFO_DESCRIPTION_INITIALIZE_REPLICATION_SERVER_PORT_DESTINATION=Directory \ server administration port number of the destination server whose contents will be initialized INFO_REPLICATION_TOOL_DESCRIPTION=This utility can be used to configure \ replication between servers so that the data of the servers is synchronized.\ replication between servers so that the data of the servers is synchronized. \ For replication to work you must first enable replication using the \ '%s' subcommand and then initialize the contents of one of \ the servers with the contents of the other using the '%s' subcommand @@ -546,6 +546,10 @@ replication configuration of the base DN's of the servers defined in the \ registration information. If no base DN's are specified as parameter the \ information for all base DN's is displayed INFO_DESCRIPTION_SUBCMD_PURGE_HISTORICAL=Launches a purge processing of the \ historical informations stored in the user entries by replication. Since this \ processing may take a while, you must specify the maximum duration for this \ processing. SEVERE_ERR_REPLICATION_NO_BASE_DN_PROVIDED=You must provide at least one base \ DN in no interactive mode. SEVERE_ERR_REPLICATION_NO_ADMINISTRATOR_PASSWORD_PROVIDED=You must provide the \ @@ -657,9 +661,9 @@ MILD_ERR_NO_SUFFIXES_AVAILABLE_TO_INITIALIZE_LOCAL_REPLICATION=There are no \ base DN's replicated in the server. MILD_ERR_REPLICATION_DISABLE_SUFFIXES_NOT_FOUND=The following base DN's could \ not be found on the server:%n%s not be found in the server:%n%s MILD_ERR_REPLICATION_INITIALIZE_LOCAL_SUFFIXES_NOT_FOUND=The following base \ DN's could not be found on the server:%n%s DN's could not be found in the server:%n%s MILD_ERR_NO_SUFFIXES_SELECTED_TO_DISABLE=You must choose at least one \ base DN to be disabled. MILD_ERR_NO_SUFFIXES_SELECTED_TO_INITIALIZE_ALL=You must choose at least one \ @@ -676,7 +680,7 @@ also the replication server (changelog and replication port) to be disabled \ you must also specify the '--%s' or '--%s' option. INFO_REPLICATION_DISABLE_ALL_SUFFIXES_DISABLE_REPLICATION_SERVER=You have \ chosen to disable all the replicated base DN's on the server '%s'. Do you \ chosen to disable all the replicated base DN's in the server '%s'. Do you \ want to disable also the replication port '%d'? INFO_DISABLE_REPLICATION_ONE_POINT_OF_FAILURE=You have decided to disable the \ replication server (replication changelog). After disabling the replication \ @@ -933,6 +937,7 @@ INFO_REPLICATION_POST_EXTERNAL_INITIALIZATION_MENU_PROMPT=Post External \ Initialization INFO_REPLICATION_STATUS_MENU_PROMPT=Display Replication Status INFO_REPLICATION_PURGE_HISTORICAL_MENU_PROMPT=Purge Historical INFO_REPLICATION_POST_ENABLE_INFO=Replication has been successfully enabled. \ Note that for replication to work you must initialize the contents of the \ base DN's that are being replicated (use %s %s to do so). @@ -2993,3 +2998,63 @@ INFO_REQUIRED_ICON_ACCESSIBLE_DESCRIPTION=Required Icon. INFO_WARNING_ICON_ACCESSIBLE_DESCRIPTION=Warning Icon. INFO_ACCESSIBLE_TABLE_CELL_NAME=%s - Column %s INFO_ERROR_DURING_PURGE_HISTORICAL_NO_LOG=Unexpected error during \ the operation. Task state: %s. Check the error logs of %s for more \ information. INFO_ERROR_DURING_PURGE_HISTORICAL_LOG=Unexpected error during the \ operation. Last log details: %s. Task state: %s. Check the error logs of \ %s for more information. SEVERE_ERR_POOLING_PURGE_HISTORICAL=Error reading the progress of \ the operation. INFO_PROGRESS_PURGE_HISTORICAL=Purging historical on base DNs:%s%s INFO_PROGRESS_PURGE_HISTORICAL_FINISHED_PROCEDURE=Purge of historical \ has been successfully completed MILD_ERR_HISTORICAL_CANNOT_BE_PURGED_ON_BASEDN=The following base DN's cannot \ be purged because they are not replicated. SEVERE_ERR_NO_SUFFIXES_AVAILABLE_TO_PURGE_HISTORICAL=There are no base DN's \ available to purge historical. MILD_ERR_REPLICATION_PURGE_SUFFIXES_NOT_FOUND=The following base DN's could \ not be found in the server:%n%s MILD_ERR_NO_SUFFIXES_SELECTED_TO_PURGE_HISTORICAL=You must choose at least one \ base DN to be purged from historical. INFO_REPLICATION_PURGE_HISTORICAL_PROMPT=Purge historical on base DN %s? INFO_REPLICATION_PURGE_HISTORICAL_MAXIMUM_DURATION_PROMPT=Maximum duration for \ the historical purge in seconds? SEVERE_ERR_LAUNCHING_PURGE_HISTORICAL=Error launching the operation. INFO_REPLICATION_PURGE_HISTORICAL_LOCAL_PROMPT=Do you want to execute the \ purge on the local server (which is stopped)? INFO_REPLICATION_PURGE_HISTORICAL_LOCAL_ENVIRONMENT=Initializing environment \ for purge historical INFO_REPLICATION_PURGE_HISTORICAL_LOCAL_STARTING=Purging historical started. SEVERE_ERR_REPLICATION_PURGE_HISTORICAL_TIMEOUT=The specified maximum time of \ %d seconds was elapsed before the purge historical completed. SEVERE_ERR_REPLICATION_PURGE_HISTORICAL_EXECUTING=An error occurred executing \ the purge historical. Details: %s.%nYou can check the error logs of the \ local server for more information. INFO_RUN_TASK_NOW=Launch now INFO_RUN_TASK_LATER=Launch later INFO_SCHEDULE_TASK=Schedule to run the task periodically INFO_TASK_FAILED_DEPENDENCY_ACTION_PROMPT=Which action must this task take if \ one if its dependent tasks fails? INFO_TASK_HAS_DEPENDENCIES_PROMPT=Has this task a dependency on another task? INFO_TASK_DEPENDENCIES_PROMPT=ID of the tasks this task depends on [continue]: INFO_HAS_ERROR_NOTIFICATION_PROMPT=Do you want to send an email notification \ if this task fails? INFO_ERROR_NOTIFICATION_PROMPT=Email addresses to send the error notification \ to [continue]: MILD_ERR_INVALID_EMAIL_ADDRESS=The value '%s' is not a valid email address. INFO_HAS_COMPLETION_NOTIFICATION_PROMPT=Do you want to send an email \ notification when this task completes? INFO_COMPLETION_NOTIFICATION_PROMPT=Email addresses to send the completion \ notification to [continue]: INFO_TASK_SCHEDULE_PROMPT=Specify when the task '%s' will be launched. INFO_TASK_START_DATE_PROMPT=Launch date (in YYYYMMDDhhmmssZ or YYYYMMDDhhmmss \ format): INFO_TASK_RECURRING_SCHEDULE_PROMPT=Periodical schedule when the \ task must run (in crontab(5) format): INFO_PURGE_HISTORICAL_TASK_NAME=Purge historical INFO_TASK_SCHEDULE_PROMPT_HEADER=>>>> Specify task scheduling parameters SEVERE_ERR_DEPENDENCY_TASK_NOT_DEFINED=There is no task with ID '%s' in the \ server. INFO_AVAILABLE_DEFINED_TASKS=The available defined tasks are:%s opends/src/messages/messages/backend.properties
@@ -20,7 +20,7 @@ # # CDDL HEADER END # # Copyright 2006-2009 Sun Microsystems, Inc. # Copyright 2006-2010 Sun Microsystems, Inc. @@ -1150,3 +1150,15 @@ to an attribute syntax that is already implemented MILD_ERR_SCHEMA_MODIFY_CANNOT_DECODE_LDAP_SYNTAX_418=An error occurred while \ attempting to decode the ldapsyntax description "%s": %s SEVERE_ERR_RECURRINGTASK_INVALID_N_TOKENS_SIMPLE_419=The provided recurring \ task schedule value has an invalid number of tokens SEVERE_ERR_RECURRINGTASK_INVALID_MINUTE_TOKEN_SIMPLE_420=The provided \ recurring task schedule value has an invalid minute token SEVERE_ERR_RECURRINGTASK_INVALID_HOUR_TOKEN_SIMPLE_421=The provided \ recurring task schedule value has an invalid hour token SEVERE_ERR_RECURRINGTASK_INVALID_DAY_TOKEN_SIMPLE_422=The provided \ recurring task schedule value has an invalid day of the month token SEVERE_ERR_RECURRINGTASK_INVALID_MONTH_TOKEN_SIMPLE_423=The provided \ recurring task schedule value has an invalid month of the year token SEVERE_ERR_RECURRINGTASK_INVALID_WEEKDAY_TOKEN_SIMPLE_424=The provided \ recurring task schedule value has an invalid day of the week token opends/src/messages/messages/tools.properties
@@ -674,7 +674,8 @@ restarted INFO_STOPDS_DESCRIPTION_STOP_TIME_384=Indicates the date/time at which the \ shutdown operation will begin as a server task expressed in format \ 'YYYYMMDDhhmmss'. A value of '0' will cause the shutdown to be scheduled for \ YYYYMMDDhhmmssZ for UTC time or YYYYMMDDhhmmss for local time. A value of \ '0' will cause the shutdown to be scheduled for \ immediate execution. When this option is specified the operation will be \ scheduled to start at the specified time after which this utility will exit \ immediately @@ -2153,13 +2154,14 @@ SEVERE_ERR_TASK_CLIENT_TASK_STATE_UNKNOWN_1455=State for task '%s' cannot be \ determined INFO_DESCRIPTION_START_DATETIME_1456=Indicates the date/time at which this \ operation will start when scheduled as a server task expressed in format \ 'YYYYMMDDhhmmss'. A value of '0' will cause the task to be scheduled for \ operation will start when scheduled as a server task expressed in \ YYYYMMDDhhmmssZ format for UTC time or YYYYMMDDhhmmss for local time. A \ value of '0' will cause the task to be scheduled for \ immediate execution. When this option is specified the operation will be \ scheduled to start at the specified time after which this utility will exit \ immediately SEVERE_ERR_START_DATETIME_FORMAT_1457=The start date/time must in format \ 'YYYYMMDDhhmmss' SEVERE_ERR_START_DATETIME_FORMAT_1457=The start date/time must in \ YYYYMMDDhhmmssZ format for UTC time or YYYYMMDDhhmmss for local time INFO_TASK_TOOL_TASK_SCHEDULED_FUTURE_1458=%s task %s scheduled to start %s SEVERE_ERR_TASK_TOOL_START_TIME_NO_LDAP_1459=You have provided options for \ scheduling this operation as a task but options provided for connecting to \ @@ -2574,4 +2576,9 @@ SEVERE_ERR_CLIENT_SIDE_TIMEOUT_1714=A client side timeout occurred.\ %nAdditional Information: %s INFO_LABEL_DBTEST_INDEX_UNDEFINED_RECORD_COUNT_1715=Undefined INFO_MAXIMUM_DURATION_PLACEHOLDER_1716={maximum duration} INFO_DESCRIPTION_PURGE_HISTORICAL_MAXIMUM_DURATION_1717=This argument specifies \ the maximum duration the purge processing must last expressed in seconds SEVERE_ERR_RECURRING_SCHEDULE_FORMAT_ERROR_1718=The provided schedule value \ has an invalid format. The schedule must be expressed using a crontab(5) \ format. Error details: %s opends/src/messages/messages/utility.properties
@@ -621,4 +621,6 @@ setting file permissions for the LDIF file %s: %s MILD_ERR_LDIF_READ_ATTR_SKIP_301=Skipping entry %s because the following error \ was received when reading its attributes: %s SEVERE_ERR_LDAP_CONN_BAD_INTEGER_302=Invalid integer number "%s". Please \ enter a valid integer opends/src/quicksetup/org/opends/quicksetup/UserData.java
@@ -84,7 +84,8 @@ private SuffixesToReplicateOptions suffixesToReplicateOptions; private Map<ServerDescriptor, AuthenticationData> remoteWithNoReplicationPort; private final Map<ServerDescriptor, AuthenticationData> remoteWithNoReplicationPort; private boolean quiet; @@ -880,7 +881,8 @@ { return new String[] { "backup.offline", "encode-password", "export-ldif.offline", "backup.offline", "dsreplication.offline", "encode-password", "export-ldif.offline", IMPORT_SCRIPT_NAME, "ldif-diff", "ldifmodify", "ldifsearch", "make-ldif", "rebuild-index", "restore.offline", SERVER_SCRIPT_NAME, "upgrade", "verify-index", "dbtest" opends/src/server/org/opends/server/admin/client/cli/TaskScheduleArgs.java
New file @@ -0,0 +1,383 @@ /* * 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 * * * Copyright 2010 Sun Microsystems, Inc. */ package org.opends.server.admin.client.cli; import static org.opends.messages.ToolMessages.*; import static org.opends.server.tools.ToolConstants.*; import java.text.ParseException; import java.util.Collections; import java.util.Date; import java.util.EnumSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import org.opends.server.backends.task.FailedDependencyAction; import org.opends.server.backends.task.RecurringTask; import org.opends.server.types.DirectoryException; import org.opends.server.util.StaticUtils; import org.opends.server.util.args.Argument; import org.opends.server.util.args.ArgumentException; import org.opends.server.util.args.StringArgument; import org.opends.server.util.cli.CLIException; /** * A class that contains all the arguments related to the task scheduling. * */ public class TaskScheduleArgs { /** * Magic value used to indicate that the user would like to schedule * this operation to run immediately as a task as opposed to running * the operation in the local VM. */ public static final String NOW = "0"; /** * Argument for describing the task's start time. */ public StringArgument startArg; /** * Argument to indicate a recurring task. */ public StringArgument recurringArg; /** * Argument for specifying completion notifications. */ public StringArgument completionNotificationArg; /** * Argument for specifying error notifications. */ public StringArgument errorNotificationArg; /** * Argument for specifying dependency. */ public StringArgument dependencyArg; /** * Argument for specifying a failed dependency action. */ public StringArgument failedDependencyActionArg; /** * Default constructor. */ public TaskScheduleArgs() { try { createTaskArguments(); } catch (ArgumentException ae) { // This is a bug. throw new RuntimeException("Unexpected error: "+ae, ae); } } private void createTaskArguments() throws ArgumentException { startArg = new StringArgument(OPTION_LONG_START_DATETIME, OPTION_SHORT_START_DATETIME, OPTION_LONG_START_DATETIME, false, false, true, INFO_START_DATETIME_PLACEHOLDER.get(), null, null, INFO_DESCRIPTION_START_DATETIME.get()); recurringArg = new StringArgument(OPTION_LONG_RECURRING_TASK, OPTION_SHORT_RECURRING_TASK, OPTION_LONG_RECURRING_TASK, false, false, true, INFO_RECURRING_TASK_PLACEHOLDER.get(), null, null, INFO_DESCRIPTION_RECURRING_TASK.get()); completionNotificationArg = new StringArgument( OPTION_LONG_COMPLETION_NOTIFICATION_EMAIL, OPTION_SHORT_COMPLETION_NOTIFICATION_EMAIL, OPTION_LONG_COMPLETION_NOTIFICATION_EMAIL, false, true, true, INFO_EMAIL_ADDRESS_PLACEHOLDER.get(), null, null, INFO_DESCRIPTION_TASK_COMPLETION_NOTIFICATION.get()); errorNotificationArg = new StringArgument( OPTION_LONG_ERROR_NOTIFICATION_EMAIL, OPTION_SHORT_ERROR_NOTIFICATION_EMAIL, OPTION_LONG_ERROR_NOTIFICATION_EMAIL, false, true, true, INFO_EMAIL_ADDRESS_PLACEHOLDER.get(), null, null, INFO_DESCRIPTION_TASK_ERROR_NOTIFICATION.get()); dependencyArg = new StringArgument(OPTION_LONG_DEPENDENCY, OPTION_SHORT_DEPENDENCY, OPTION_LONG_DEPENDENCY, false, true, true, INFO_TASK_ID_PLACEHOLDER.get(), null, null, INFO_DESCRIPTION_TASK_DEPENDENCY_ID.get()); Set<FailedDependencyAction> fdaValSet = EnumSet.allOf(FailedDependencyAction.class); failedDependencyActionArg = new StringArgument( OPTION_LONG_FAILED_DEPENDENCY_ACTION, OPTION_SHORT_FAILED_DEPENDENCY_ACTION, OPTION_LONG_FAILED_DEPENDENCY_ACTION, false, true, true, INFO_ACTION_PLACEHOLDER.get(), null, null, INFO_DESCRIPTION_TASK_FAILED_DEPENDENCY_ACTION.get(StaticUtils .collectionToString(fdaValSet, ","), FailedDependencyAction .defaultValue().name())); for (Argument arg : getArguments()) { arg.setPropertyName(arg.getLongIdentifier()); } } /** * Returns all the task schedule related arguments. * @return all the task schedule related arguments. */ public Argument[] getArguments() { return new Argument[] {startArg, recurringArg, completionNotificationArg, errorNotificationArg, dependencyArg, failedDependencyActionArg}; } /** * Validates arguments related to task scheduling. This should be * called after the <code>ArgumentParser.parseArguments</code> has * been called. * <br> * Note that this method does only validation that is not dependent on whether * the operation will be launched as a task or not. If the operation is not * to be launched as a task, the method {@link #validateArgsIfOffline()} * should be called instead of this method. * @throws ArgumentException if there is a problem with the arguments. * @throws CLIException if there is a problem with one of the values provided * by the user. */ public void validateArgs() throws ArgumentException, CLIException { if (startArg.isPresent() && !NOW.equals(startArg.getValue())) { try { Date date = StaticUtils.parseDateTimeString(startArg.getValue()); // Check that the provided date is not previous to the current date. Date currentDate = new Date(System.currentTimeMillis()); if (currentDate.after(date)) { throw new CLIException(ERR_START_DATETIME_ALREADY_PASSED.get( startArg.getValue())); } } catch (ParseException pe) { throw new ArgumentException(ERR_START_DATETIME_FORMAT.get()); } } if (recurringArg.isPresent()) { try { RecurringTask.parseTaskTab(recurringArg.getValue()); } catch (DirectoryException de) { throw new ArgumentException(ERR_RECURRING_SCHEDULE_FORMAT_ERROR.get( de.getMessageObject()), de); } } if (completionNotificationArg.isPresent()) { LinkedList<String> addrs = completionNotificationArg.getValues(); for (String addr : addrs) { if (!StaticUtils.isEmailAddress(addr)) { throw new ArgumentException(ERR_TASKTOOL_INVALID_EMAIL_ADDRESS.get( addr, completionNotificationArg.getLongIdentifier())); } } } if (errorNotificationArg.isPresent()) { LinkedList<String> addrs = errorNotificationArg.getValues(); for (String addr : addrs) { if (!StaticUtils.isEmailAddress(addr)) { throw new ArgumentException(ERR_TASKTOOL_INVALID_EMAIL_ADDRESS.get( addr, errorNotificationArg.getLongIdentifier())); } } } if (failedDependencyActionArg.isPresent()) { if (!dependencyArg.isPresent()) { throw new ArgumentException(ERR_TASKTOOL_FDA_WITH_NO_DEPENDENCY.get()); } String fda = failedDependencyActionArg.getValue(); if (null == FailedDependencyAction.fromString(fda)) { Set<FailedDependencyAction> fdaValSet = EnumSet.allOf(FailedDependencyAction.class); throw new ArgumentException(ERR_TASKTOOL_INVALID_FDA.get(fda, StaticUtils.collectionToString(fdaValSet, ","))); } } } /** * Validates arguments related to task scheduling. This should be * called after the <code>ArgumentParser.parseArguments</code> has * been called. * <br> * This method assumes that the operation is not to be launched as a task. * This method covers all the checks done by {@link #validateArgs()}, so it * is not necessary to call that method if this method is being called. * @throws ArgumentException if there is a problem with the arguments. * @throws CLIException if there is a problem with one of the values provided * by the user. */ public void validateArgsIfOffline() throws ArgumentException, CLIException { Argument[] incompatibleArgs = {startArg, recurringArg, completionNotificationArg, errorNotificationArg, dependencyArg, failedDependencyActionArg}; for (Argument arg : incompatibleArgs) { if (arg.isPresent()) { throw new ArgumentException(ERR_TASKTOOL_OPTIONS_FOR_TASK_ONLY.get( arg.getLongIdentifier())); } } validateArgs(); } /** * Gets the date at which the associated task should be scheduled to start. * * @return date/time at which the task should be scheduled */ public Date getStartDateTime() { Date start = null; // If the start time arg is present parse its value if (startArg != null && startArg.isPresent()) { if (NOW.equals(startArg.getValue())) { start = new Date(); } else { try { start = StaticUtils.parseDateTimeString(startArg.getValue()); } catch (ParseException pe) { // ignore; validated in validateTaskArgs() } } } return start; } /** * Whether the arguments provided by the user, indicate that the task should * be executed immediately. * <br> * This method assumes that the arguments have already been parsed and * validated. * @return {@code true} if the task must be executed immediately and * {@code false} otherwise. */ public boolean isStartNow() { boolean isStartNow = true; if (startArg != null && startArg.isPresent()) { isStartNow = NOW.equals(startArg.getValue()); } return isStartNow; } /** * Gets the date/time pattern for recurring task schedule. * * @return recurring date/time pattern at which the task * should be scheduled. */ public String getRecurringDateTime() { String pattern = null; // If the recurring task arg is present parse its value if (recurringArg != null && recurringArg.isPresent()) { pattern = recurringArg.getValue(); } return pattern; } /** * Gets a list of task IDs upon which the associated task is dependent. * * @return list of task IDs */ public List<String> getDependencyIds() { if (dependencyArg.isPresent()) { return dependencyArg.getValues(); } else { return Collections.emptyList(); } } /** * Gets the action to take should one of the dependent task fail. * * @return action to take */ public FailedDependencyAction getFailedDependencyAction() { FailedDependencyAction fda = null; if (failedDependencyActionArg.isPresent()) { String fdaString = failedDependencyActionArg.getValue(); fda = FailedDependencyAction.fromString(fdaString); } return fda; } /** * Gets a list of email address to which an email will be sent when this * task completes. * * @return list of email addresses */ public List<String> getNotifyUponCompletionEmailAddresses() { if (completionNotificationArg.isPresent()) { return completionNotificationArg.getValues(); } else { return Collections.emptyList(); } } /** * Gets a list of email address to which an email will be sent if this * task encounters an error during execution. * * @return list of email addresses */ public List<String> getNotifyUponErrorEmailAddresses() { if (errorNotificationArg.isPresent()) { return errorNotificationArg.getValues(); } else { return Collections.emptyList(); } } } opends/src/server/org/opends/server/backends/task/RecurringTask.java
@@ -64,6 +64,9 @@ /** * This class defines a information about a recurring task, which will be used * to repeatedly schedule tasks for processing. * <br> * It also provides some static methods that allow to validate strings in * crontab (5) format. */ public class RecurringTask { @@ -102,6 +105,12 @@ */ private static enum TaskTab {MINUTE, HOUR, DAY, MONTH, WEEKDAY}; private final static int MINUTE_INDEX = 0; private final static int HOUR_INDEX = 1; private final static int DAY_INDEX = 2; private final static int MONTH_INDEX = 3; private final static int WEEKDAY_INDEX = 4; // Exact match pattern. private static final Pattern exactPattern = Pattern.compile("\\d+"); @@ -115,11 +124,11 @@ Pattern.compile("^(\\d+,)(.*)(\\d+)$"); // Boolean arrays holding task tab slots. private boolean[] minutesArray; private boolean[] hoursArray; private boolean[] daysArray; private boolean[] monthArray; private boolean[] weekdayArray; private final boolean[] minutesArray; private final boolean[] hoursArray; private final boolean[] daysArray; private final boolean[] monthArray; private final boolean[] weekdayArray; /** * Creates a new recurring task based on the information in the provided @@ -225,7 +234,16 @@ } String taskScheduleTab = value.toString(); parseTaskTab(taskScheduleTab); boolean[][] taskArrays = new boolean[][]{null, null, null, null, null}; parseTaskTab(taskScheduleTab, taskArrays, true); minutesArray = taskArrays[MINUTE_INDEX]; hoursArray = taskArrays[HOUR_INDEX]; daysArray = taskArrays[DAY_INDEX]; monthArray = taskArrays[MONTH_INDEX]; weekdayArray = taskArrays[WEEKDAY_INDEX]; // Get the class name from the entry. If there isn't one, then fail. attrType = DirectoryServer.getAttributeType( @@ -451,14 +469,41 @@ * @param taskSchedule recurring task schedule tab in crontab(5) format. * @throws DirectoryException to indicate an error. */ private void parseTaskTab(String taskSchedule) throws DirectoryException public static void parseTaskTab(String taskSchedule) throws DirectoryException { parseTaskTab(taskSchedule, new boolean[][]{null, null, null, null, null}, false); } /** * Parse and validate recurring task schedule. * @param taskSchedule recurring task schedule tab in crontab(5) format. * @param arrays, an array of 5 boolean arrays. The array has the following * structure: {minutesArray, hoursArray, daysArray, monthArray, weekdayArray}. * @param referToTaskEntryAttribute whether the error messages must refer * to the task entry attribute or not. This is used to have meaningful * messages when the {@link #parseTaskTab(String)} is called to validate * a crontab formatted string. * @throws DirectoryException to indicate an error. */ private static void parseTaskTab(String taskSchedule, boolean[][] arrays, boolean referToTaskEntryAttribute) throws DirectoryException { StringTokenizer st = new StringTokenizer(taskSchedule); if (st.countTokens() != TASKTAB_NUM_TOKENS) { throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, ERR_RECURRINGTASK_INVALID_N_TOKENS.get( ATTR_RECURRING_TASK_SCHEDULE)); if (referToTaskEntryAttribute) { throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, ERR_RECURRINGTASK_INVALID_N_TOKENS.get( ATTR_RECURRING_TASK_SCHEDULE)); } else { throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, ERR_RECURRINGTASK_INVALID_N_TOKENS_SIMPLE.get()); } } for (TaskTab taskTabToken : TaskTab.values()) { @@ -466,47 +511,87 @@ switch (taskTabToken) { case MINUTE: try { minutesArray = parseTaskTabField(token, 0, 59); arrays[MINUTE_INDEX] = parseTaskTabField(token, 0, 59); } catch (IllegalArgumentException e) { throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, ERR_RECURRINGTASK_INVALID_MINUTE_TOKEN.get( ATTR_RECURRING_TASK_SCHEDULE)); if (referToTaskEntryAttribute) { throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, ERR_RECURRINGTASK_INVALID_MINUTE_TOKEN.get( ATTR_RECURRING_TASK_SCHEDULE)); } else { throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, ERR_RECURRINGTASK_INVALID_MINUTE_TOKEN_SIMPLE.get()); } } break; case HOUR: try { hoursArray = parseTaskTabField(token, 0, 23); arrays[HOUR_INDEX] = parseTaskTabField(token, 0, 23); } catch (IllegalArgumentException e) { throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, ERR_RECURRINGTASK_INVALID_HOUR_TOKEN.get( ATTR_RECURRING_TASK_SCHEDULE)); if (referToTaskEntryAttribute) { throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, ERR_RECURRINGTASK_INVALID_HOUR_TOKEN.get( ATTR_RECURRING_TASK_SCHEDULE)); } else { throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, ERR_RECURRINGTASK_INVALID_HOUR_TOKEN_SIMPLE.get()); } } break; case DAY: try { daysArray = parseTaskTabField(token, 1, 31); arrays[DAY_INDEX] = parseTaskTabField(token, 1, 31); } catch (IllegalArgumentException e) { throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, ERR_RECURRINGTASK_INVALID_DAY_TOKEN.get( ATTR_RECURRING_TASK_SCHEDULE)); if (referToTaskEntryAttribute) { throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, ERR_RECURRINGTASK_INVALID_DAY_TOKEN.get( ATTR_RECURRING_TASK_SCHEDULE)); } else { throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, ERR_RECURRINGTASK_INVALID_DAY_TOKEN_SIMPLE.get()); } } break; case MONTH: try { monthArray = parseTaskTabField(token, 1, 12); arrays[MONTH_INDEX] = parseTaskTabField(token, 1, 12); } catch (IllegalArgumentException e) { throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, ERR_RECURRINGTASK_INVALID_MONTH_TOKEN.get( ATTR_RECURRING_TASK_SCHEDULE)); if (referToTaskEntryAttribute) { throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, ERR_RECURRINGTASK_INVALID_MONTH_TOKEN.get( ATTR_RECURRING_TASK_SCHEDULE)); } else { throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, ERR_RECURRINGTASK_INVALID_MONTH_TOKEN_SIMPLE.get()); } } break; case WEEKDAY: try { weekdayArray = parseTaskTabField(token, 0, 6); arrays[WEEKDAY_INDEX] = parseTaskTabField(token, 0, 6); } catch (IllegalArgumentException e) { throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, ERR_RECURRINGTASK_INVALID_WEEKDAY_TOKEN.get( ATTR_RECURRING_TASK_SCHEDULE)); if (referToTaskEntryAttribute) { throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, ERR_RECURRINGTASK_INVALID_WEEKDAY_TOKEN.get( ATTR_RECURRING_TASK_SCHEDULE)); } else { throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, ERR_RECURRINGTASK_INVALID_WEEKDAY_TOKEN_SIMPLE.get()); } } break; } opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java
@@ -5680,7 +5680,9 @@ attrs, null); int count = 0; task.setProgressStats(lastChangeNumberPurgedFromHist, count); if (task != null) task.setProgressStats(lastChangeNumberPurgedFromHist, count); LinkedList<SearchResultEntry> entries = searchOp.getSearchEntries(); for (SearchResultEntry entry : entries) @@ -5731,7 +5733,8 @@ } else { task.setProgressStats(lastChangeNumberPurgedFromHist, count); if (task != null) task.setProgressStats(lastChangeNumberPurgedFromHist, count); } } } opends/src/server/org/opends/server/tasks/PurgeConflictsHistoricalTask.java
@@ -62,6 +62,11 @@ public class PurgeConflictsHistoricalTask extends Task { /** * The default value for the maximum duration of the purge expressed in * seconds. */ public static final int DEFAULT_MAX_DURATION = 60 * 60; /** * The tracer object for the debug logger. */ private static final DebugTracer TRACER = getTracer(); @@ -69,14 +74,6 @@ private String domainString = null; private LDAPReplicationDomain domain = null; // The last changeNumber purged : help the user to know how well the purge // processing has done its job. // We want to help the user know: // - the task has started at dateX time, will end at dateY max // and is currently purging dateZ long currentCNPurgedDate = 0; long taskMaxEndDate = 0; /** * current historical purge delay * <---------------------------------> @@ -95,7 +92,7 @@ * */ long purgeTaskMaxDurationInSec = 3600; // Default:1h private int purgeTaskMaxDurationInSec = DEFAULT_MAX_DURATION; TaskState initState; @@ -112,6 +109,7 @@ /** * {@inheritDoc} */ @Override public Message getDisplayName() { return TaskMessages.INFO_TASK_PURGE_CONFLICTS_HIST_NAME.get(); } @@ -162,7 +160,7 @@ { try { purgeTaskMaxDurationInSec = Long.decode(maxDurationStringInSec); purgeTaskMaxDurationInSec = Integer.decode(maxDurationStringInSec); } catch(Exception e) { @@ -177,6 +175,7 @@ /** * {@inheritDoc} */ @Override protected TaskState runTask() { Boolean purgeCompletedInTime = false; opends/src/server/org/opends/server/tools/dsreplication/LocalPurgeHistorical.java
New file @@ -0,0 +1,226 @@ /* * 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 * * * Copyright 2010 Sun Microsystems, Inc. */ package org.opends.server.tools.dsreplication; import static org.opends.messages.AdminToolMessages.*; import static org.opends.messages.CoreMessages.*; import java.io.File; import java.util.logging.Level; import java.util.logging.Logger; import org.opends.messages.Message; import org.opends.quicksetup.util.ProgressMessageFormatter; import org.opends.server.replication.plugin.LDAPReplicationDomain; import org.opends.server.types.DN; import org.opends.server.types.DirectoryEnvironmentConfig; import org.opends.server.types.DirectoryException; import org.opends.server.types.OpenDsException; import org.opends.server.types.ResultCode; import org.opends.server.util.EmbeddedUtils; import org.opends.server.util.StaticUtils; import org.opends.server.util.TimeThread; import org.opends.server.util.cli.ConsoleApplication; import org.opends.server.util.cli.PointAdder; /** * The class that is in charge of taking the different information provided * by the user through the command-line and actually executing the local * purge. * */ public class LocalPurgeHistorical { private static final Logger LOG = Logger.getLogger(LocalPurgeHistorical.class.getName()); private final PurgeHistoricalUserData uData; private final ConsoleApplication app; private final ProgressMessageFormatter formatter; private final String configFile; private final String configClass; /** * The default constructor. * @param uData the object containing the information provided by the user. * @param app the console application that is used to write the progress * and error messages. * @param formatter the formatter to be used to generated the messages. * @param configFile the file that contains the configuration. This is * required to initialize properly the server. * @param configClass the class to be used to read the configuration. This is * required to initialize properly the server. */ public LocalPurgeHistorical(PurgeHistoricalUserData uData, ConsoleApplication app, ProgressMessageFormatter formatter, String configFile, String configClass) { this.uData = uData; this.app = app; this.formatter = formatter; this.configFile = configFile; this.configClass = configClass; } /** * Executes the purge historical operation locally. * @return the result code. */ public ReplicationCliReturnCode execute() { boolean applyTimeout = uData.getMaximumDuration() > 0; long startTime = TimeThread.getTime(); long purgeMaxTime = getTimeoutInSeconds() * 1000; long endMaxTime = startTime + purgeMaxTime; app.printProgress(formatter.getFormattedProgress( INFO_REPLICATION_PURGE_HISTORICAL_LOCAL_ENVIRONMENT.get())); PointAdder pointAdder = new PointAdder(app); pointAdder.start(); LDAPReplicationDomain domain = null; Class<?> cfgClass; try { cfgClass = Class.forName(configClass); } catch (Exception e) { pointAdder.stop(); Message message = ERR_CANNOT_LOAD_CONFIG_HANDLER_CLASS.get( configClass, StaticUtils.stackTraceToSingleLineString(e)); app.println(message); LOG.log(Level.SEVERE, "Error loading configuration class "+configClass+ ": "+e, e); return ReplicationCliReturnCode.ERROR_LOCAL_PURGE_HISTORICAL_CLASS_LOAD; } try { // Create a configuration for the server. DirectoryEnvironmentConfig environmentConfig = new DirectoryEnvironmentConfig(); environmentConfig.setConfigClass(cfgClass); environmentConfig.setConfigFile(new File(configFile)); environmentConfig.setDisableConnectionHandlers(true); EmbeddedUtils.startServer(environmentConfig); } catch (OpenDsException ode) { pointAdder.stop(); Message message = ode.getMessageObject(); ERR_CANNOT_LOAD_CONFIG_HANDLER_CLASS.get( configClass, StaticUtils.stackTraceToSingleLineString(ode)); app.println(message); LOG.log(Level.SEVERE, "Error starting server with file "+configFile+ ": "+ode, ode); return ReplicationCliReturnCode.ERROR_LOCAL_PURGE_HISTORICAL_SERVER_START; } pointAdder.stop(); app.printProgress(formatter.getFormattedDone()); app.printlnProgress(); app.printlnProgress(); app.printProgress(formatter.getFormattedProgress( INFO_REPLICATION_PURGE_HISTORICAL_LOCAL_STARTING.get())); app.printlnProgress(); if (applyTimeout && timeoutOccurred(endMaxTime)) { return handleTimeout(); } try { // launch the job for (String baseDN : uData.getBaseDNs()) { DN dn = DN.decode(baseDN); // We can assume that this is an LDAP replication domain domain = LDAPReplicationDomain.retrievesReplicationDomain(dn); domain.purgeConflictsHistorical(null, startTime + purgeMaxTime); } } catch (DirectoryException de) { if (de.getResultCode() == ResultCode.ADMIN_LIMIT_EXCEEDED) { return handleTimeout(); } else { return handleGenericExecuting(de); } } return ReplicationCliReturnCode.SUCCESSFUL; } private ReplicationCliReturnCode handleGenericExecuting(OpenDsException ode) { LOG.log(Level.SEVERE, "Error executing purge historical: "+ode, ode); app.println(); app.println(ERR_REPLICATION_PURGE_HISTORICAL_EXECUTING.get( ode.getMessageObject())); return ReplicationCliReturnCode.ERROR_LOCAL_PURGE_HISTORICAL_EXECUTING; } private ReplicationCliReturnCode handleTimeout() { app.println(); app.println(ERR_REPLICATION_PURGE_HISTORICAL_TIMEOUT.get( getTimeoutInSeconds())); return ReplicationCliReturnCode.ERROR_LOCAL_PURGE_HISTORICAL_TIMEOUT; } /** * Returns the time-out provided by the user in seconds. * @return the time-out provided by the user in seconds. */ private int getTimeoutInSeconds() { return uData.getMaximumDuration(); } /** * A method that tells whether the maximum time to execute the operation was * elapsed or not. * @param endMaxTime the latest time in milliseconds when the operation should * be completed. * @return {@code true} if the time-out occurred and {@code false} otherwise. */ private boolean timeoutOccurred(long endMaxTime) { return TimeThread.getTime() > endMaxTime; } } opends/src/server/org/opends/server/tools/dsreplication/PurgeHistoricalScheduleInformation.java
New file @@ -0,0 +1,157 @@ /* * 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 * * * Copyright 2010 Sun Microsystems, Inc. */ package org.opends.server.tools.dsreplication; import java.util.ArrayList; import java.util.Date; import java.util.List; import org.opends.server.backends.task.FailedDependencyAction; import org.opends.server.config.ConfigConstants; import org.opends.server.protocols.ldap.LDAPAttribute; import org.opends.server.tools.tasks.TaskScheduleInformation; import org.opends.server.tools.tasks.TaskScheduleUserData; import org.opends.server.types.ByteString; import org.opends.server.types.RawAttribute; /** * This is a simple adaptor to create a task schedule information object * using the data provided by the user. It is used to be able to share some * code with the {@link TaskTool} class. * */ public class PurgeHistoricalScheduleInformation implements TaskScheduleInformation { private final PurgeHistoricalUserData uData; private TaskScheduleUserData taskSchedule; /** * Default constructor. * @param uData the data provided by the user to do the purge historical. */ public PurgeHistoricalScheduleInformation( PurgeHistoricalUserData uData) { this.uData = uData; this.taskSchedule = uData.getTaskSchedule(); if (taskSchedule == null) { taskSchedule = new TaskScheduleUserData(); } } /** * {@inheritDoc} */ public void addTaskAttributes(List<RawAttribute> attributes) { ArrayList<ByteString> baseDNs = new ArrayList<ByteString>(); for (String baseDN : uData.getBaseDNs()) { baseDNs.add(ByteString.valueOf(baseDN)); } attributes.add(new LDAPAttribute( ConfigConstants.ATTR_TASK_CONFLICTS_HIST_PURGE_DOMAIN_DN, baseDNs)); attributes.add(new LDAPAttribute( ConfigConstants.ATTR_TASK_CONFLICTS_HIST_PURGE_MAX_DURATION, Long.toString(uData.getMaximumDuration()))); } /** * {@inheritDoc} */ public List<String> getDependencyIds() { return taskSchedule.getDependencyIds(); } /** * {@inheritDoc} */ public FailedDependencyAction getFailedDependencyAction() { return taskSchedule.getFailedDependencyAction(); } /** * {@inheritDoc} */ public List<String> getNotifyUponCompletionEmailAddresses() { return taskSchedule.getNotifyUponCompletionEmailAddresses(); } /** * {@inheritDoc} */ public List<String> getNotifyUponErrorEmailAddresses() { return taskSchedule.getNotifyUponErrorEmailAddresses(); } /** * {@inheritDoc} */ public String getRecurringDateTime() { return taskSchedule.getRecurringDateTime(); } /** * {@inheritDoc} */ public Date getStartDateTime() { return taskSchedule.getStartDate(); } /** * {@inheritDoc} */ public Class<?> getTaskClass() { return org.opends.server.tasks.PurgeConflictsHistoricalTask.class; } /** * {@inheritDoc} */ public String getTaskId() { return null; } /** * {@inheritDoc} */ public String getTaskObjectclass() { return "ds-task-purge-conflicts-historical"; } } opends/src/server/org/opends/server/tools/dsreplication/PurgeHistoricalUserData.java
New file @@ -0,0 +1,313 @@ /* * 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 * * * Copyright 2010 Sun Microsystems, Inc. */ package org.opends.server.tools.dsreplication; import java.util.ArrayList; import java.util.LinkedList; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.naming.directory.Attribute; import javax.naming.directory.BasicAttribute; import javax.naming.directory.BasicAttributes; import org.opends.server.admin.client.cli.TaskScheduleArgs; import org.opends.server.tools.tasks.TaskClient; import org.opends.server.tools.tasks.TaskScheduleUserData; import org.opends.server.types.ByteString; import org.opends.server.types.RawAttribute; /** * This class is used to store the information provided by the user to * purge historical data. * */ public class PurgeHistoricalUserData extends MonoServerReplicationUserData { private int maximumDuration; private boolean online; private TaskScheduleUserData taskSchedule = new TaskScheduleUserData(); /** * Default constructor. */ public PurgeHistoricalUserData() { } /** * Returns the maximum duration that the purge can take in seconds. * @return the maximum duration that the purge can take in seconds. */ public int getMaximumDuration() { return maximumDuration; } /** * Sets the maximum duration that the purge can take in seconds. * @param maximumDuration the maximum duration that the purge can take in * seconds. */ public void setMaximumDuration(int maximumDuration) { this.maximumDuration = maximumDuration; } /** * Whether the task will be executed on an online server (using an LDAP * connection and the tasks backend) or not. * @return {@code true} if the task will be executed on an online server * and {@code false} otherwise. */ public boolean isOnline() { return online; } /** * Sets whether the task will be executed on an online server or not. * @param online {@code true} if the task will be executed on an online server * and {@code false} otherwise. */ public void setOnline(boolean online) { this.online = online; } /** * Returns the object describing the schedule of the task. If the operation * is not online, the value returned by this method should not be taken into * account. * @return the object describing the schedule of the task. */ public TaskScheduleUserData getTaskSchedule() { return taskSchedule; } /** * Sets the object describing the schedule of the task. * @param taskSchedule the object describing the schedule of the task. */ public void setTaskSchedule(TaskScheduleUserData taskSchedule) { this.taskSchedule = taskSchedule; } /** * Initializes the contents of the provided purge historical replication user * data object with what was provided in the command-line without prompting to * the user. * @param uData the purge historical replication user data object to be * initialized. * @param argParser the argument parser with the arguments provided by the * user. */ public static void initializeWithArgParser(PurgeHistoricalUserData uData, ReplicationCliArgumentParser argParser) { uData.setBaseDNs(new LinkedList<String>(argParser.getBaseDNs())); if (argParser.connectionArgumentsPresent()) { String adminUid = getValue(argParser.getAdministratorUID(), argParser.getDefaultAdministratorUID()); uData.setAdminUid(adminUid); String adminPwd = argParser.getBindPasswordAdmin(); uData.setAdminPwd(adminPwd); String hostName = getValue(argParser.getHostNameToStatus(), argParser.getDefaultHostNameToStatus()); uData.setHostName(hostName); int port = getValue(argParser.getPortToStatus(), argParser.getDefaultPortToStatus()); uData.setPort(port); uData.setOnline(true); TaskScheduleUserData taskSchedule = new TaskScheduleUserData(); TaskScheduleArgs taskArgs = argParser.getTaskArgsList(); taskSchedule.setStartNow(taskArgs.isStartNow()); if (!taskSchedule.isStartNow()) { taskSchedule.setStartDate(taskArgs.getStartDateTime()); taskSchedule.setDependencyIds(taskArgs.getDependencyIds()); taskSchedule.setFailedDependencyAction( taskArgs.getFailedDependencyAction()); taskSchedule.setNotifyUponErrorEmailAddresses( taskArgs.getNotifyUponErrorEmailAddresses()); taskSchedule.setNotifyUponCompletionEmailAddresses( taskArgs.getNotifyUponCompletionEmailAddresses()); taskSchedule.setRecurringDateTime( taskArgs.getRecurringDateTime()); } uData.setTaskSchedule(taskSchedule); } else { uData.setOnline(false); } uData.setMaximumDuration(getValue(argParser.getMaximumDuration(), argParser.getDefaultMaximumDuration())); } /** * Commodity method that simply checks if a provided value is null or not, * if it is not <CODE>null</CODE> returns it and if it is <CODE>null</CODE> * returns the provided default value. * @param v the value to analyze. * @param defaultValue the default value. * @return if the provided value is not <CODE>null</CODE> returns it and if it * is <CODE>null</CODE> returns the provided default value. */ private static String getValue(String v, String defaultValue) { if (v != null) { return v; } else { return defaultValue; } } /** * Commodity method that simply checks if a provided value is -1 or not, * if it is not -1 returns it and if it is -1 returns the provided default * value. * @param v the value to analyze. * @param defaultValue the default value. * @return if the provided value is not -1 returns it and if it is -1 returns * the provided default value. */ private static int getValue(int v, int defaultValue) { if (v != -1) { return v; } else { return defaultValue; } } /** * Commodity method that returns the list of basic task attributes required * to launch a task corresponding to the provided user data. * @param uData the user data describing the purge historical to be executed. * @return the list of basic task attributes required * to launch a task corresponding to the provided user data. */ public static BasicAttributes getTaskAttributes(PurgeHistoricalUserData uData) { PurgeHistoricalScheduleInformation information = new PurgeHistoricalScheduleInformation(uData); ArrayList<RawAttribute> rawAttrs = TaskClient.getTaskAttributes(information); BasicAttributes attrs = getAttributes(rawAttrs); return attrs; } private static BasicAttributes getAttributes(ArrayList<RawAttribute> rawAttrs) { BasicAttributes attrs = new BasicAttributes(); for (RawAttribute rawAttr : rawAttrs) { BasicAttribute attr = new BasicAttribute(rawAttr.getAttributeType()); for (ByteString v : rawAttr.getValues()) { attr.add(v.toString()); } attrs.put(attr); } return attrs; } /** * Returns the DN of the task corresponding to the provided list of * attributes. The code assumes that the attributes have been generated * calling the method {@link #getTaskAttributes(PurgeHistoricalUserData)}. * @param attrs the attributes of the task entry. * @return the DN of the task entry. */ public static String getTaskDN(BasicAttributes attrs) { ArrayList<RawAttribute> rawAttrs = getRawAttributes(attrs); return TaskClient.getTaskDN(rawAttrs); } /** * Returns the ID of the task corresponding to the provided list of * attributes. The code assumes that the attributes have been generated * calling the method {@link #getTaskAttributes(PurgeHistoricalUserData)}. * @param attrs the attributes of the task entry. * @return the ID of the task entry. */ public static String getTaskID(BasicAttributes attrs) { ArrayList<RawAttribute> rawAttrs = getRawAttributes(attrs); return TaskClient.getTaskID(rawAttrs); } private static ArrayList<RawAttribute> getRawAttributes(BasicAttributes attrs) { ArrayList<RawAttribute> rawAttrs = new ArrayList<RawAttribute>(); NamingEnumeration<Attribute> nAtt = attrs.getAll(); try { while (nAtt.hasMore()) { Attribute attr = nAtt.next(); NamingEnumeration<?> values = attr.getAll(); ArrayList<ByteString> rawValues = new ArrayList<ByteString>(); while (values.hasMore()) { Object v = values.next(); rawValues.add(ByteString.valueOf(v.toString())); } RawAttribute rAttr = RawAttribute.create(attr.getID(), rawValues); rawAttrs.add(rAttr); } } catch (NamingException ne) { // This is a bug. throw new RuntimeException("Unexpected error: "+ne, ne); } return rawAttrs; } } opends/src/server/org/opends/server/tools/dsreplication/ReplicationCliArgumentParser.java
@@ -44,6 +44,10 @@ import org.opends.server.admin.AdministrationConnector; import org.opends.server.admin.client.cli.SecureConnectionCliArgs; import org.opends.server.admin.client.cli.SecureConnectionCliParser; import org.opends.server.admin.client.cli.TaskScheduleArgs; import org.opends.server.extensions.ConfigFileHandler; import org.opends.server.tasks.PurgeConflictsHistoricalTask; import org.opends.server.types.OpenDsException; import org.opends.server.util.args.Argument; import org.opends.server.util.args.ArgumentException; import org.opends.server.util.args.ArgumentGroup; @@ -68,6 +72,7 @@ private SubCommand postExternalInitializationSubCmd; private SubCommand preExternalInitializationSubCmd; private SubCommand statusReplicationSubCmd; private SubCommand purgeHistoricalSubCmd; int defaultAdminPort = AdministrationConnector.DEFAULT_ADMINISTRATION_CONNECTOR_PORT; @@ -219,7 +224,7 @@ /** * The 'suffixes' global argument. */ private StringArgument baseDNsArg = null; StringArgument baseDNsArg = null; /** * The 'quiet' argument. @@ -259,6 +264,22 @@ */ BooleanArgument advancedArg; // The argument set by the user to specify the configuration class // (useful when dsreplication purge-historical runs locally/starts the server) private StringArgument configClassArg; // The argument set by the user to specify the configuration file // (useful when dsreplication purge-historical runs locally/starts the server) private StringArgument configFileArg; TaskScheduleArgs taskArgs; /** * The 'maximumDuration' argument for the purge of historical. */ IntegerArgument maximumDurationArg; /** * The text of the enable replication subcommand. */ @@ -297,6 +318,11 @@ */ public static final String STATUS_REPLICATION_SUBCMD_NAME = "status"; /** * The text of the purge historical subcommand. */ public static final String PURGE_HISTORICAL_SUBCMD_NAME = "purge-historical"; // This CLI is always using the administration connector with SSL private final boolean alwaysSSL = true; @@ -329,6 +355,7 @@ public void initializeParser(OutputStream outStream) throws ArgumentException { taskArgs = new TaskScheduleArgs(); initializeGlobalArguments(outStream); try { @@ -345,6 +372,7 @@ createPreExternalInitializationSubCommand(); createPostExternalInitializationSubCommand(); createStatusReplicationSubCommand(); createPurgeHistoricalSubCommand(); } /** @@ -365,6 +393,7 @@ /** * {@inheritDoc} */ @Override public int validateGlobalOptions(MessageBuilder buf) { int returnValue; @@ -418,7 +447,8 @@ { errors.add(ERR_REPLICATION_NO_BASE_DN_PROVIDED.get()); } if (getBindPasswordAdmin() == null) if (getBindPasswordAdmin() == null && !isPurgeHistoricalSubcommand()) { errors.add(ERR_REPLICATION_NO_ADMINISTRATOR_PASSWORD_PROVIDED.get( "--"+secureArgsList.bindPasswordArg.getLongIdentifier(), @@ -556,6 +586,23 @@ INFO_REPLICATION_DESCRIPTION_ADVANCED.get()); defaultArgs.add(index++, advancedArg); configClassArg = new StringArgument("configclass", OPTION_SHORT_CONFIG_CLASS, OPTION_LONG_CONFIG_CLASS, true, false, true, INFO_CONFIGCLASS_PLACEHOLDER.get(), ConfigFileHandler.class.getName(), null, INFO_DESCRIPTION_CONFIG_CLASS.get()); configClassArg.setHidden(true); defaultArgs.add(index++, configClassArg); configFileArg = new StringArgument("configfile", 'f', "configFile", true, false, true, INFO_CONFIGFILE_PLACEHOLDER.get(), null, null, INFO_DESCRIPTION_CONFIG_FILE.get()); configFileArg.setHidden(true); defaultArgs.add(index++, configFileArg); for (int i=0; i<index; i++) { Argument arg = defaultArgs.get(i); @@ -585,6 +632,7 @@ * @throws ArgumentException if there is a conflict with the provided * arguments. */ @Override protected void initializeGlobalArguments( Collection<Argument> args, ArgumentGroup argGroup) @@ -625,6 +673,8 @@ port1Arg = new IntegerArgument("port1", OPTION_SHORT_PORT, "port1", false, false, true, INFO_PORT_PLACEHOLDER.get(), defaultAdminPort, null, true, 1, true, 65336, INFO_DESCRIPTION_ENABLE_REPLICATION_SERVER_PORT1.get()); bindDn1Arg = new StringArgument("bindDN1", OPTION_SHORT_BINDDN, @@ -645,6 +695,8 @@ replicationPort1Arg = new IntegerArgument("replicationPort1", 'r', "replicationPort1", false, false, true, INFO_PORT_PLACEHOLDER.get(), 8989, null, true, 1, true, 65336, INFO_DESCRIPTION_ENABLE_REPLICATION_PORT1.get()); secureReplication1Arg = new BooleanArgument("secureReplication1", null, @@ -666,6 +718,8 @@ port2Arg = new IntegerArgument("port2", null, "port2", false, false, true, INFO_PORT_PLACEHOLDER.get(), defaultAdminPort, null, true, 1, true, 65336, INFO_DESCRIPTION_ENABLE_REPLICATION_SERVER_PORT2.get()); bindDn2Arg = new StringArgument("bindDN2", null, @@ -686,6 +740,8 @@ replicationPort2Arg = new IntegerArgument("replicationPort2", 'R', "replicationPort2", false, false, true, INFO_PORT_PLACEHOLDER.get(), 8989, null, true, 1, true, 65336, INFO_DESCRIPTION_ENABLE_REPLICATION_PORT2.get()); secureReplication2Arg = new BooleanArgument("secureReplication2", null, @@ -783,6 +839,8 @@ portSourceArg = new IntegerArgument("portSource", OPTION_SHORT_PORT, "portSource", false, false, true, INFO_PORT_PLACEHOLDER.get(), defaultAdminPort, null, true, 1, true, 65336, INFO_DESCRIPTION_INITIALIZE_REPLICATION_SERVER_PORT_SOURCE.get()); hostNameDestinationArg = new StringArgument("hostDestination", 'O', @@ -794,6 +852,8 @@ "portDestination", false, false, true, INFO_PORT_PLACEHOLDER.get(), defaultAdminPort, null, true, 1, true, 65336, INFO_DESCRIPTION_INITIALIZE_REPLICATION_SERVER_PORT_DESTINATION.get()); initializeReplicationSubCmd = new SubCommand(this, @@ -916,6 +976,50 @@ } /** * Creates the purge historical subcommand and all the specific options * for the subcommand. Note: this method assumes that * initializeGlobalArguments has already been called and that hostNameArg and * portArg have been created. */ private void createPurgeHistoricalSubCommand() throws ArgumentException { maximumDurationArg = new IntegerArgument( "maximumDuration", null, // shortId "maximumDuration", true, // isRequired false, // isMultivalued true, // needsValue INFO_MAXIMUM_DURATION_PLACEHOLDER.get(), PurgeConflictsHistoricalTask.DEFAULT_MAX_DURATION, null, true, 0, false, Integer.MAX_VALUE, INFO_DESCRIPTION_PURGE_HISTORICAL_MAXIMUM_DURATION.get()); purgeHistoricalSubCmd = new SubCommand( this, PURGE_HISTORICAL_SUBCMD_NAME, INFO_DESCRIPTION_SUBCMD_PURGE_HISTORICAL.get()); Argument[] argsToAdd = { secureArgsList.hostNameArg, secureArgsList.portArg, maximumDurationArg}; for (int i=0; i<argsToAdd.length; i++) { argsToAdd[i].setPropertyName(argsToAdd[i].getLongIdentifier()); purgeHistoricalSubCmd.addArgument(argsToAdd[i]); } for (Argument arg : taskArgs.getArguments()) { purgeHistoricalSubCmd.addArgument(arg); } } /** * Tells whether the user specified to have an interactive operation or not. * This method must be called after calling parseArguments. * @return <CODE>true</CODE> if the user specified to have an interactive @@ -1061,6 +1165,7 @@ * Returns the Administrator UID explicitly provided in the command-line. * @return the Administrator UID explicitly provided in the command-line. */ @Override public String getAdministratorUID() { return getValue(secureArgsList.adminUidArg); @@ -1642,6 +1747,28 @@ } /** * Returns the config class value provided in the hidden argument of the * command-line. * @return the config class value provided in the hidden argument of the * command-line. */ public String getConfigClass() { return getValue(configClassArg); } /** * Returns the config file value provided in the hidden argument of the * command-line. * @return the config file value provided in the hidden argument of the * command-line. */ public String getConfigFile() { return getValue(configFileArg); } /** * Returns the value of the provided argument only if the user provided it * explicitly. * @param arg the StringArgument to be handled. @@ -1752,6 +1879,10 @@ { validatePostExternalInitializationOptions(buf); } else if (isPurgeHistoricalSubcommand()) { validatePurgeHistoricalOptions(buf); } else { @@ -1762,6 +1893,35 @@ } /** * Checks the purge historical subcommand options and updates the * provided MessageBuilder with the errors that were encountered with the * subcommand options. * * This method assumes that the method parseArguments for the parser has * already been called. * @param buf the MessageBuilder object where we add the error messages * describing the errors encountered. */ private void validatePurgeHistoricalOptions(MessageBuilder buf) { try { if (!isInteractive() && !connectionArgumentsPresent()) { taskArgs.validateArgsIfOffline(); } else { taskArgs.validateArgs(); } } catch (OpenDsException ode) { addMessage(buf, ode.getMessageObject()); } } /** * Returns whether the user provided subcommand is the enable replication * or not. * @return <CODE>true</CODE> if the user provided subcommand is the @@ -1795,6 +1955,17 @@ } /** * Returns whether the user provided subcommand is the purge historical * or not. * @return <CODE>true</CODE> if the user provided subcommand is the * purge historical and <CODE>false</CODE> otherwise. */ public boolean isPurgeHistoricalSubcommand() { return isSubcommand(PURGE_HISTORICAL_SUBCMD_NAME); } /** * Returns whether the user provided subcommand is the initialize all * replication or not. * @return <CODE>true</CODE> if the user provided subcommand is the @@ -2071,4 +2242,60 @@ { return secureArgsList; } /** * Returns the TaskScheduleArgs object containing the arguments * of this parser. * @return the TaskScheduleArgs object containing the arguments * of this parser. */ public TaskScheduleArgs getTaskArgsList() { return taskArgs; } /** * Returns whether the user specified connection arguments or not. * @return {@code true} if the user specified connection arguments and * {@code false} otherwise. */ public boolean connectionArgumentsPresent() { if (isPurgeHistoricalSubcommand()) { boolean secureArgsPresent = getSecureArgsList() != null && getSecureArgsList().argumentsPresent(); // This have to be explicitly specified because their original definition // has been replaced. boolean adminArgsPresent = secureArgsList.adminUidArg.isPresent() || secureArgsList.bindPasswordArg.isPresent() || secureArgsList.bindPasswordFileArg.isPresent(); return secureArgsPresent || adminArgsPresent; } else { return true; } } /** * Returns the maximum duration explicitly provided in the purge historical * replication subcommand. * @return the maximum duration explicitly provided in the purge historical * replication subcommand. Returns -1 if no port was explicitly provided. */ public int getMaximumDuration() { return getValue(maximumDurationArg); } /** * Returns the maximum duration default value in the purge historical * replication subcommand. * @return the maximum duration default value in the purge historical * replication subcommand. */ public int getDefaultMaximumDuration() { return getDefaultValue(maximumDurationArg); } } opends/src/server/org/opends/server/tools/dsreplication/ReplicationCliMain.java
@@ -49,6 +49,7 @@ import java.io.PrintStream; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; @@ -56,6 +57,7 @@ import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedSet; @@ -90,11 +92,18 @@ import org.opends.admin.ads.util.ConnectionUtils; import org.opends.admin.ads.util.PreferredConnection; import org.opends.admin.ads.util.ServerLoader; import org.opends.guitools.controlpanel.datamodel.BackendDescriptor; import org.opends.guitools.controlpanel.datamodel.BaseDNDescriptor; import org.opends.guitools.controlpanel.util.ConfigFromDirContext; import org.opends.guitools.controlpanel.util.ConfigFromFile; import org.opends.guitools.controlpanel.util.ControlPanelLog; import org.opends.guitools.controlpanel.util.ProcessReader; import org.opends.guitools.controlpanel.util.Utilities; import org.opends.messages.Message; import org.opends.messages.MessageBuilder; import org.opends.quicksetup.ApplicationException; import org.opends.quicksetup.Constants; import org.opends.quicksetup.Installation; import org.opends.quicksetup.ReturnCode; import org.opends.quicksetup.event.ProgressUpdateEvent; import org.opends.quicksetup.event.ProgressUpdateListener; @@ -116,14 +125,19 @@ import org.opends.server.admin.std.meta.*; import org.opends.server.config.ConfigException; import org.opends.server.core.DirectoryServer; import org.opends.server.tasks.PurgeConflictsHistoricalTask; import org.opends.server.tools.ClientException; import org.opends.server.tools.ToolConstants; import org.opends.server.tools.tasks.TaskEntry; import org.opends.server.tools.tasks.TaskScheduleInteraction; import org.opends.server.tools.tasks.TaskScheduleUserData; import org.opends.server.types.DN; import org.opends.server.types.InitializationException; import org.opends.server.types.NullOutputStream; import org.opends.server.types.OpenDsException; import org.opends.server.util.ServerConstants; import org.opends.server.util.SetupUtils; import org.opends.server.util.StaticUtils; import org.opends.server.util.args.Argument; import org.opends.server.util.args.ArgumentException; import org.opends.server.util.args.BooleanArgument; @@ -136,6 +150,7 @@ import org.opends.server.util.cli.LDAPConnectionConsoleInteraction; import org.opends.server.util.cli.MenuBuilder; import org.opends.server.util.cli.MenuResult; import org.opends.server.util.cli.PointAdder; import org.opends.server.util.table.TableBuilder; import org.opends.server.util.table.TextTablePrinter; @@ -158,6 +173,20 @@ /** Suffix for log files. */ static public final String LOG_FILE_SUFFIX = ".log"; /** * Property used to call the dsreplication script and ReplicationCliMain to * know which are the java properties to be used (those of dsreplication or * those of dsreplication.offline). */ private static final String SCRIPT_CALL_STATUS = "org.opends.server.dsreplicationcallstatus"; /** * The value set by the dsreplication script if it is called the first time. */ private static final String FIRST_SCRIPT_CALL = "firstcall"; private boolean forceNonInteractive; private static final Logger LOG = @@ -204,6 +233,10 @@ */ STATUS(INFO_REPLICATION_STATUS_MENU_PROMPT.get()), /** * Replication purge historical. */ PURGE_HISTORICAL(INFO_REPLICATION_PURGE_HISTORICAL_MENU_PROMPT.get()), /** * Cancel operation. */ CANCEL(null); @@ -389,7 +422,7 @@ returnValue = ERROR_USER_DATA; } } if (initializeServer) if (initializeServer && returnValue == SUCCESSFUL_NOP) { DirectoryServer.bootstrapClient(); @@ -484,6 +517,12 @@ subCommand = ReplicationCliArgumentParser.STATUS_REPLICATION_SUBCMD_NAME; } else if (argParser.isPurgeHistoricalSubcommand()) { returnValue = purgeHistorical(); subCommand = ReplicationCliArgumentParser.PURGE_HISTORICAL_SUBCMD_NAME; } else { if (argParser.isInteractive()) @@ -526,6 +565,11 @@ ReplicationCliArgumentParser.STATUS_REPLICATION_SUBCMD_NAME; break; case PURGE_HISTORICAL: subCommand = ReplicationCliArgumentParser.PURGE_HISTORICAL_SUBCMD_NAME; break; default: // User canceled returnValue = USER_CANCELLED; @@ -573,6 +617,11 @@ return returnValue.getReturnCode(); } private boolean isFirstCallFromScript() { return FIRST_SCRIPT_CALL.equals(System.getProperty(SCRIPT_CALL_STATUS)); } private void createArgumenParser() throws ArgumentException { argParser = new ReplicationCliArgumentParser(CLASS_NAME); @@ -783,6 +832,663 @@ } /** * Based on the data provided in the command-line it displays replication * status. * @return the error code if the operation failed and SUCCESSFUL if it was * successful. */ private ReplicationCliReturnCode purgeHistorical() { ReplicationCliReturnCode returnValue; PurgeHistoricalUserData uData = new PurgeHistoricalUserData(); if (argParser.isInteractive()) { uData = new PurgeHistoricalUserData(); if (promptIfRequired(uData)) { returnValue = purgeHistorical(uData); } else { returnValue = USER_CANCELLED; } } else { initializeWithArgParser(uData); returnValue = purgeHistorical(uData); } return returnValue; } /** * Initializes the contents of the provided purge historical replication user * data object with what was provided in the command-line without prompting to * the user. * @param uData the purge historical replication user data object to be * initialized. */ private void initializeWithArgParser(PurgeHistoricalUserData uData) { PurgeHistoricalUserData.initializeWithArgParser(uData, argParser); } private ReplicationCliReturnCode purgeHistorical( PurgeHistoricalUserData uData) { ReplicationCliReturnCode returnValue = null; if (uData.isOnline()) { returnValue = purgeHistoricalRemotely(uData); } else { returnValue = purgeHistoricalLocally(uData); } return returnValue; } private ReplicationCliReturnCode purgeHistoricalLocally( PurgeHistoricalUserData uData) { ReplicationCliReturnCode returnValue; LinkedList<String> baseDNs = uData.getBaseDNs(); checkSuffixesForLocalPurgeHistorical(baseDNs, false); if (!baseDNs.isEmpty()) { uData.setBaseDNs(baseDNs); printPurgeHistoricalEquivalentIfRequired(uData); returnValue = SUCCESSFUL; try { returnValue = purgeHistoricalLocallyTask(uData); } catch (ReplicationCliException rce) { println(); println(getCriticalExceptionMessage(rce)); returnValue = rce.getErrorCode(); LOG.log(Level.SEVERE, "Complete error stack:", rce); } } else { returnValue = HISTORICAL_CANNOT_BE_PURGED_ON_BASEDN; } return returnValue; } private void printPurgeProgressMessage(PurgeHistoricalUserData uData) { String separator = formatter.getLineBreak().toString() + formatter.getTab().toString(); printlnProgress(); Message msg = formatter.getFormattedProgress( INFO_PROGRESS_PURGE_HISTORICAL.get(separator, Utils.getStringFromCollection(uData.getBaseDNs(), separator))); printProgress(msg); printlnProgress(); } private ReplicationCliReturnCode purgeHistoricalLocallyTask( PurgeHistoricalUserData uData) throws ReplicationCliException { ReplicationCliReturnCode returnCode = ReplicationCliReturnCode.SUCCESSFUL; if (isFirstCallFromScript()) { // Launch the process: launch dsreplication in non-interactive mode with // the recursive property set. ArrayList<String> args = new ArrayList<String>(); args.add(getCommandLinePath(getCommandName())); args.add(ReplicationCliArgumentParser.PURGE_HISTORICAL_SUBCMD_NAME); args.add("--"+argParser.noPromptArg.getLongIdentifier()); args.add("--"+argParser.maximumDurationArg.getLongIdentifier()); args.add(String.valueOf(uData.getMaximumDuration())); for (String baseDN : uData.getBaseDNs()) { args.add("--"+argParser.baseDNsArg.getLongIdentifier()); args.add(baseDN); } ProcessBuilder pb = new ProcessBuilder(args); // Use the java args in the script. Map<String, String> env = pb.environment(); env.put("RECURSIVE_LOCAL_CALL", "true"); try { ProcessReader outReader = null; ProcessReader errReader = null; Process process = pb.start(); outReader = new ProcessReader(process, getOutputStream(), false); errReader = new ProcessReader(process, getErrorStream(), true); outReader.startReading(); errReader.startReading(); int code = process.waitFor(); for (ReplicationCliReturnCode c : ReplicationCliReturnCode.values()) { if (c.getReturnCode() == code) { returnCode = c; break; } } } catch (Exception e) { Message msg = ERR_LAUNCHING_PURGE_HISTORICAL.get(); ReplicationCliReturnCode code = ERROR_LAUNCHING_PURGE_HISTORICAL; throw new ReplicationCliException( getThrowableMsg(msg, e), code, e); } } else { printPurgeProgressMessage(uData); LocalPurgeHistorical localPurgeHistorical = new LocalPurgeHistorical(uData, this, formatter, argParser.getConfigFile(), argParser.getConfigClass()); returnCode = localPurgeHistorical.execute(); if (returnCode == ReplicationCliReturnCode.SUCCESSFUL) { printSuccessMessage(uData, null); } } return returnCode; } private void printPurgeHistoricalEquivalentIfRequired( PurgeHistoricalUserData uData) { if (mustPrintCommandBuilder()) { try { CommandBuilder commandBuilder = createCommandBuilder( ReplicationCliArgumentParser.PURGE_HISTORICAL_SUBCMD_NAME, uData); printCommandBuilder(commandBuilder); } catch (Throwable t) { LOG.log(Level.SEVERE, "Error printing equivalente command-line: "+t, t); } } } private ReplicationCliReturnCode purgeHistoricalRemotely( PurgeHistoricalUserData uData) { ReplicationCliReturnCode returnValue = SUCCESSFUL_NOP; InitialLdapContext ctx = null; // Connect to the provided server try { ctx = createAdministrativeContext(uData.getHostName(), uData.getPort(), useSSL, useStartTLS, ADSContext.getAdministratorDN(uData.getAdminUid()), uData.getAdminPwd(), getConnectTimeout(), getTrustManager()); } catch (NamingException ne) { String hostPort = getServerRepresentation(uData.getHostName(), uData.getPort()); println(); println(getMessageForException(ne, hostPort)); LOG.log(Level.SEVERE, "Complete error stack:", ne); } if (ctx != null) { LinkedList<String> baseDNs = uData.getBaseDNs(); checkSuffixesForPurgeHistorical(baseDNs, ctx, false); if (!baseDNs.isEmpty()) { uData.setBaseDNs(baseDNs); printPurgeHistoricalEquivalentIfRequired(uData); returnValue = SUCCESSFUL; try { returnValue = purgeHistoricalRemoteTask(ctx, uData); } catch (ReplicationCliException rce) { println(); println(getCriticalExceptionMessage(rce)); returnValue = rce.getErrorCode(); LOG.log(Level.SEVERE, "Complete error stack:", rce); } } else { returnValue = HISTORICAL_CANNOT_BE_PURGED_ON_BASEDN; } } else { returnValue = ERROR_CONNECTING; } if (ctx != null) { try { ctx.close(); } catch (Throwable t) { } } return returnValue; } private void printSuccessMessage(PurgeHistoricalUserData uData, String taskID) { printlnProgress(); if (!uData.isOnline()) { printProgress( INFO_PROGRESS_PURGE_HISTORICAL_FINISHED_PROCEDURE.get()); } else if (uData.getTaskSchedule().isStartNow()) { printProgress(INFO_TASK_TOOL_TASK_SUCESSFULL.get( INFO_PURGE_HISTORICAL_TASK_NAME.get(), taskID)); } else if (uData.getTaskSchedule().getStartDate() != null) { printProgress(INFO_TASK_TOOL_TASK_SCHEDULED_FUTURE.get( INFO_PURGE_HISTORICAL_TASK_NAME.get(), taskID, StaticUtils.formatDateTimeString( uData.getTaskSchedule().getStartDate()))); } else { printProgress(INFO_TASK_TOOL_RECURRING_TASK_SCHEDULED.get( INFO_PURGE_HISTORICAL_TASK_NAME.get(), taskID)); } printlnProgress(); } /** * Launches the purge historical operation using the * provided connection. * @param ctx the connection to the server. * @throws ReplicationCliException if there is an error performing the * operation. */ private ReplicationCliReturnCode purgeHistoricalRemoteTask( InitialLdapContext ctx, PurgeHistoricalUserData uData) throws ReplicationCliException { printPurgeProgressMessage(uData); ReplicationCliReturnCode returnCode = ReplicationCliReturnCode.SUCCESSFUL; boolean taskCreated = false; int i = 0; boolean isOver = false; String dn = null; String taskID = null; while (!taskCreated) { BasicAttributes attrs = PurgeHistoricalUserData.getTaskAttributes(uData); dn = PurgeHistoricalUserData.getTaskDN(attrs); taskID = PurgeHistoricalUserData.getTaskID(attrs); try { DirContext dirCtx = ctx.createSubcontext(dn, attrs); taskCreated = true; LOG.log(Level.INFO, "created task entry: "+attrs); dirCtx.close(); } catch (NameAlreadyBoundException x) { } catch (NamingException ne) { LOG.log(Level.SEVERE, "Error creating task "+attrs, ne); Message msg = ERR_LAUNCHING_PURGE_HISTORICAL.get(); ReplicationCliReturnCode code = ERROR_LAUNCHING_PURGE_HISTORICAL; throw new ReplicationCliException( getThrowableMsg(msg, ne), code, ne); } i++; } // Wait until it is over SearchControls searchControls = new SearchControls(); searchControls.setCountLimit(1); searchControls.setSearchScope( SearchControls. OBJECT_SCOPE); String filter = "objectclass=*"; searchControls.setReturningAttributes( new String[] { "ds-task-log-message", "ds-task-state", "ds-task-purge-conflicts-historical-purged-values-count", "ds-task-purge-conflicts-historical-purge-completed-in-time", "ds-task-purge-conflicts-historical-purge-completed-in-time", "ds-task-purge-conflicts-historical-last-purged-changenumber" }); String lastLogMsg = null; // Polling only makes sense when we are recurrently scheduling a task // or the task is being executed now. while (!isOver && (uData.getTaskSchedule().getStartDate() == null)) { try { Thread.sleep(500); } catch (Throwable t) { } try { NamingEnumeration<SearchResult> res = ctx.search(dn, filter, searchControls); SearchResult sr = null; try { sr = res.next(); } finally { res.close(); } String logMsg = getFirstValue(sr, "ds-task-log-message"); if (logMsg != null) { if (!logMsg.equals(lastLogMsg)) { LOG.log(Level.INFO, logMsg); lastLogMsg = logMsg; } } InstallerHelper helper = new InstallerHelper(); String state = getFirstValue(sr, "ds-task-state"); if (helper.isDone(state) || helper.isStoppedByError(state)) { isOver = true; Message errorMsg; String server = ConnectionUtils.getHostPort(ctx); if (lastLogMsg == null) { errorMsg = INFO_ERROR_DURING_PURGE_HISTORICAL_NO_LOG.get( state, server); } else { errorMsg = INFO_ERROR_DURING_PURGE_HISTORICAL_LOG.get( lastLogMsg, state, server); } if (helper.isCompletedWithErrors(state)) { LOG.log(Level.WARNING, "Completed with error: "+errorMsg); println(errorMsg); } else if (!helper.isSuccessful(state) || helper.isStoppedByError(state)) { LOG.log(Level.WARNING, "Error: "+errorMsg); ReplicationCliReturnCode code = ERROR_LAUNCHING_PURGE_HISTORICAL; throw new ReplicationCliException(errorMsg, code, null); } } } catch (NameNotFoundException x) { isOver = true; } catch (NamingException ne) { Message msg = ERR_POOLING_PURGE_HISTORICAL.get(); throw new ReplicationCliException( getThrowableMsg(msg, ne), ERROR_CONNECTING, ne); } } if (returnCode == ReplicationCliReturnCode.SUCCESSFUL) { printSuccessMessage(uData, taskID); } return returnCode; } /** * Checks that historical can actually be purged in the provided baseDNs * for the server. * @param suffixes the suffixes provided by the user. This Collection is * updated with the base DNs that the user provided interactively. * @param ctx connection to the server. * @param interactive whether to ask the user to provide interactively * base DNs if none of the provided base DNs can be purged. */ private void checkSuffixesForPurgeHistorical(Collection<String> suffixes, InitialLdapContext ctx, boolean interactive) { TreeSet<String> availableSuffixes = new TreeSet<String>(); TreeSet<String> notReplicatedSuffixes = new TreeSet<String>(); Collection<ReplicaDescriptor> replicas = getReplicas(ctx); for (ReplicaDescriptor rep : replicas) { String dn = rep.getSuffix().getDN(); if (rep.isReplicated()) { availableSuffixes.add(dn); } else { notReplicatedSuffixes.add(dn); } } checkSuffixesForPurgeHistorical(suffixes, availableSuffixes, notReplicatedSuffixes, interactive); } /** * Checks that historical can actually be purged in the provided baseDNs * for the local server. * @param suffixes the suffixes provided by the user. This Collection is * updated with the base DNs that the user provided interactively. * @param interactive whether to ask the user to provide interactively * base DNs if none of the provided base DNs can be purged. */ private void checkSuffixesForLocalPurgeHistorical(Collection<String> suffixes, boolean interactive) { TreeSet<String> availableSuffixes = new TreeSet<String>(); TreeSet<String> notReplicatedSuffixes = new TreeSet<String>(); Collection<ReplicaDescriptor> replicas = getLocalReplicas(); for (ReplicaDescriptor rep : replicas) { String dn = rep.getSuffix().getDN(); if (rep.isReplicated()) { availableSuffixes.add(dn); } else { notReplicatedSuffixes.add(dn); } } checkSuffixesForPurgeHistorical(suffixes, availableSuffixes, notReplicatedSuffixes, interactive); } private Collection<ReplicaDescriptor> getLocalReplicas() { Collection<ReplicaDescriptor> replicas = new ArrayList<ReplicaDescriptor>(); ConfigFromFile configFromFile = new ConfigFromFile(); configFromFile.readConfiguration(); Collection<BackendDescriptor> backends = configFromFile.getBackends(); for (BackendDescriptor backend : backends) { for (BaseDNDescriptor baseDN : backend.getBaseDns()) { SuffixDescriptor suffix = new SuffixDescriptor(); suffix.setDN(baseDN.getDn().toString()); ReplicaDescriptor replica = new ReplicaDescriptor(); if (baseDN.getType() == BaseDNDescriptor.Type.REPLICATED) { replica.setReplicationId(baseDN.getReplicaID()); } else { replica.setReplicationId(-1); } replica.setBackendName(backend.getBackendID()); replica.setSuffix(suffix); suffix.setReplicas(Collections.singleton(replica)); replicas.add(replica); } } return replicas; } private void checkSuffixesForPurgeHistorical(Collection<String> suffixes, Collection<String> availableSuffixes, Collection<String> notReplicatedSuffixes, boolean interactive) { if (availableSuffixes.size() == 0) { println(); println(ERR_NO_SUFFIXES_AVAILABLE_TO_PURGE_HISTORICAL.get()); suffixes.clear(); } else { // Verify that the provided suffixes are configured in the servers. TreeSet<String> notFound = new TreeSet<String>(); TreeSet<String> alreadyNotReplicated = new TreeSet<String>(); for (String dn : suffixes) { boolean found = false; for (String dn1 : availableSuffixes) { if (Utils.areDnsEqual(dn, dn1)) { found = true; break; } } if (!found) { boolean notReplicated = false; for (String s : notReplicatedSuffixes) { if (Utils.areDnsEqual(s, dn)) { notReplicated = true; break; } } if (notReplicated) { alreadyNotReplicated.add(dn); } else { notFound.add(dn); } } } suffixes.removeAll(notFound); suffixes.removeAll(alreadyNotReplicated); if (notFound.size() > 0) { println(); println(ERR_REPLICATION_PURGE_SUFFIXES_NOT_FOUND.get( Utils.getStringFromCollection(notFound, Constants.LINE_SEPARATOR))); } if (interactive) { boolean confirmationLimitReached = false; while (suffixes.isEmpty()) { boolean noSchemaOrAds = false; for (String s: availableSuffixes) { if (!Utils.areDnsEqual(s, ADSContext.getAdministrationSuffixDN()) && !Utils.areDnsEqual(s, Constants.SCHEMA_DN) && !Utils.areDnsEqual(s, Constants.REPLICATION_CHANGES_DN)) { noSchemaOrAds = true; } } if (!noSchemaOrAds) { // In interactive mode we do not propose to manage the // administration suffix. println(); println(ERR_NO_SUFFIXES_AVAILABLE_TO_PURGE_HISTORICAL.get()); break; } else { println(); println(ERR_NO_SUFFIXES_SELECTED_TO_PURGE_HISTORICAL.get()); for (String dn : availableSuffixes) { if (!Utils.areDnsEqual(dn, ADSContext.getAdministrationSuffixDN()) && !Utils.areDnsEqual(dn, Constants.SCHEMA_DN) && !Utils.areDnsEqual(dn, Constants.REPLICATION_CHANGES_DN)) { try { if (askConfirmation( INFO_REPLICATION_PURGE_HISTORICAL_PROMPT.get(dn), true, LOG)) { suffixes.add(dn); } } catch (CLIException ce) { println(ce.getMessageObject()); confirmationLimitReached = true; break; } } } } if (confirmationLimitReached) { suffixes.clear(); break; } } } } } /** * Based on the data provided in the command-line it initializes replication * between two servers. * @return the error code if the operation failed and SUCCESSFUL if it was @@ -811,6 +1517,186 @@ return returnValue; } /** * Updates the contents of the provided PurgeHistoricalUserData * object with the information provided in the command-line. If some * information is missing, ask the user to provide valid data. * We assume that if this method is called we are in interactive mode. * @param uData the object to be updated. * @return <CODE>true</CODE> if the object was successfully updated and * <CODE>false</CODE> if the user canceled the operation. */ private boolean promptIfRequired(PurgeHistoricalUserData uData) { boolean cancelled = false; boolean onlineSet = false; boolean firstTry = true; Boolean serverRunning = null; InitialLdapContext ctx = null; while (!cancelled && !onlineSet) { boolean promptForConnection = false; if (argParser.connectionArgumentsPresent() && firstTry) { promptForConnection = true; } else { if (serverRunning == null) { serverRunning = Utilities.isServerRunning( Installation.getLocal().getInstanceDirectory()); } if (!serverRunning) { try { printlnProgress(); promptForConnection = !askConfirmation( INFO_REPLICATION_PURGE_HISTORICAL_LOCAL_PROMPT.get(), true, LOG); } catch (CLIException ce) { println(ce.getMessageObject()); cancelled = true; } } else { promptForConnection = true; } } if (promptForConnection) { try { ci.run(); String host = ci.getHostName(); int port = ci.getPortNumber(); String adminUid = ci.getAdministratorUID(); String adminPwd = ci.getBindPassword(); ctx = createInitialLdapContextInteracting(ci); if (ctx == null) { cancelled = true; } else { uData.setOnline(true); uData.setAdminUid(adminUid); uData.setAdminPwd(adminPwd); uData.setHostName(host); uData.setPort(port); onlineSet = true; } } catch (ClientException ce) { LOG.log(Level.WARNING, "Client exception "+ce); println(); println(ce.getMessageObject()); println(); ci.resetConnectionArguments(); } catch (ArgumentException ae) { LOG.log(Level.WARNING, "Argument exception "+ae); println(); println(ae.getMessageObject()); println(); cancelled = true; } } else { uData.setOnline(false); onlineSet = true; } firstTry = false; } if (!cancelled) { int maximumDuration = argParser.getMaximumDuration(); /* Prompt for maximum duration */ if (!argParser.maximumDurationArg.isPresent()) { printlnProgress(); maximumDuration = askInteger( INFO_REPLICATION_PURGE_HISTORICAL_MAXIMUM_DURATION_PROMPT.get(), argParser.getDefaultMaximumDuration(), LOG); } uData.setMaximumDuration(maximumDuration); } if (!cancelled) { LinkedList<String> suffixes = argParser.getBaseDNs(); if (uData.isOnline()) { checkSuffixesForPurgeHistorical(suffixes, ctx, true); } else { checkSuffixesForLocalPurgeHistorical(suffixes, true); } cancelled = suffixes.isEmpty(); uData.setBaseDNs(suffixes); } if (uData.isOnline() && !cancelled) { List<? extends TaskEntry> taskEntries = getAvailableTaskEntries(ctx); TaskScheduleInteraction interaction = new TaskScheduleInteraction(uData.getTaskSchedule(), argParser.taskArgs, this, formatter, taskEntries, INFO_PURGE_HISTORICAL_TASK_NAME.get()); try { interaction.run(); } catch (CLIException ce) { println(ce.getMessageObject()); cancelled = true; } } if (ctx != null) { try { ctx.close(); } catch (Throwable t) { } } return !cancelled; } private List<? extends TaskEntry> getAvailableTaskEntries( InitialLdapContext ctx) { List<TaskEntry> taskEntries = new ArrayList<TaskEntry>(); List<OpenDsException> exceptions = new ArrayList<OpenDsException>(); ConfigFromDirContext cfg = new ConfigFromDirContext(); cfg.updateTaskInformation(ctx, exceptions, taskEntries); for (OpenDsException ode : exceptions) { LOG.log(Level.WARNING, "Error retrieving task entries: "+ode, ode); } return taskEntries; } /** * Updates the contents of the provided EnableReplicationUserData object * with the information provided in the command-line. If some information @@ -2734,7 +3620,7 @@ * Initializes the contents of the provided status replication user data * object with what was provided in the command-line without prompting to the * user. * @param uData the disable replication user data object to be initialized. * @param uData the status replication user data object to be initialized. */ private void initializeWithArgParser(StatusReplicationUserData uData) { @@ -3525,7 +4411,7 @@ } /** * Disbles the replication in the server for the provided suffixes using the * Disables the replication in the server for the provided suffixes using the * data in the DisableReplicationUserData object. This method does not prompt * to the user for information if something is missing. * @param uData the DisableReplicationUserData object. @@ -5560,7 +6446,7 @@ // done). if (adsMergeDone) { PointAdder pointAdder = new PointAdder(); PointAdder pointAdder = new PointAdder(this); printProgress( INFO_ENABLE_REPLICATION_INITIALIZING_ADS_ALL.get( ConnectionUtils.getHostPort(ctxSource))); @@ -5606,7 +6492,7 @@ } if (adsMergeDone) { PointAdder pointAdder = new PointAdder(); PointAdder pointAdder = new PointAdder(this); printProgress( INFO_ENABLE_REPLICATION_INITIALIZING_SCHEMA.get( ConnectionUtils.getHostPort(ctxDestination), @@ -8968,12 +9854,7 @@ private CommandBuilder createCommandBuilder(String subcommandName, ReplicationUserData uData) throws ArgumentException { String commandName = System.getProperty(ServerConstants.PROPERTY_SCRIPT_NAME); if (commandName == null) { commandName = "dsreplication"; } String commandName = getCommandName(); CommandBuilder commandBuilder = new CommandBuilder(commandName, subcommandName); @@ -8992,48 +9873,17 @@ updateCommandBuilder(commandBuilder, (InitializeReplicationUserData)uData); } else if (subcommandName.equals( ReplicationCliArgumentParser.PURGE_HISTORICAL_SUBCMD_NAME)) { // All the arguments for initialize replication are update here. updateCommandBuilder(commandBuilder, (PurgeHistoricalUserData)uData); } else { // Update the arguments used in the console interaction with the // actual arguments of dsreplication. if ((ci != null) && (ci.getCommandBuilder() != null)) { CommandBuilder interactionBuilder = ci.getCommandBuilder(); for (Argument arg : interactionBuilder.getArguments()) { if (arg.getLongIdentifier().equals(OPTION_LONG_BINDPWD)) { StringArgument bindPasswordArg = new StringArgument("adminPassword", OPTION_SHORT_BINDPWD, "adminPassword", false, false, true, INFO_BINDPWD_PLACEHOLDER.get(), null, null, INFO_DESCRIPTION_REPLICATION_ADMIN_BINDPASSWORD.get()); bindPasswordArg.addValue(arg.getValue()); commandBuilder.addObfuscatedArgument(bindPasswordArg); } else if (arg.getLongIdentifier().equals(OPTION_LONG_BINDPWD_FILE)) { FileBasedArgument bindPasswordFileArg = new FileBasedArgument( "adminPasswordFile", OPTION_SHORT_BINDPWD_FILE, "adminPasswordFile", false, false, INFO_BINDPWD_FILE_PLACEHOLDER.get(), null, null, INFO_DESCRIPTION_REPLICATION_ADMIN_BINDPASSWORDFILE.get()); bindPasswordFileArg.getNameToValueMap().putAll( ((FileBasedArgument)arg).getNameToValueMap()); commandBuilder.addArgument(bindPasswordFileArg); } else { if (interactionBuilder.isObfuscated(arg)) { commandBuilder.addObfuscatedArgument(arg); } else { commandBuilder.addArgument(arg); } } } } updateCommandBuilderWithConsoleInteraction(commandBuilder, ci); } if (subcommandName.equals( @@ -9063,6 +9913,97 @@ return commandBuilder; } private String getCommandName() { String commandName = System.getProperty(ServerConstants.PROPERTY_SCRIPT_NAME); if (commandName == null) { commandName = "dsreplication"; } return commandName; } private void updateCommandBuilderWithConsoleInteraction( CommandBuilder commandBuilder, LDAPConnectionConsoleInteraction ci) throws ArgumentException { if ((ci != null) && (ci.getCommandBuilder() != null)) { CommandBuilder interactionBuilder = ci.getCommandBuilder(); for (Argument arg : interactionBuilder.getArguments()) { if (arg.getLongIdentifier().equals(OPTION_LONG_BINDPWD)) { StringArgument bindPasswordArg = new StringArgument("adminPassword", OPTION_SHORT_BINDPWD, "adminPassword", false, false, true, INFO_BINDPWD_PLACEHOLDER.get(), null, null, INFO_DESCRIPTION_REPLICATION_ADMIN_BINDPASSWORD.get()); bindPasswordArg.addValue(arg.getValue()); commandBuilder.addObfuscatedArgument(bindPasswordArg); } else if (arg.getLongIdentifier().equals(OPTION_LONG_BINDPWD_FILE)) { FileBasedArgument bindPasswordFileArg = new FileBasedArgument( "adminPasswordFile", OPTION_SHORT_BINDPWD_FILE, "adminPasswordFile", false, false, INFO_BINDPWD_FILE_PLACEHOLDER.get(), null, null, INFO_DESCRIPTION_REPLICATION_ADMIN_BINDPASSWORDFILE.get()); bindPasswordFileArg.getNameToValueMap().putAll( ((FileBasedArgument)arg).getNameToValueMap()); commandBuilder.addArgument(bindPasswordFileArg); } else { if (interactionBuilder.isObfuscated(arg)) { commandBuilder.addObfuscatedArgument(arg); } else { commandBuilder.addArgument(arg); } } } } } private void updateCommandBuilder(CommandBuilder commandBuilder, PurgeHistoricalUserData uData) throws ArgumentException { if (uData.isOnline()) { updateCommandBuilderWithConsoleInteraction(commandBuilder, ci); if (uData.getTaskSchedule() != null) { updateCommandBuilderWithTaskSchedule(commandBuilder, uData.getTaskSchedule()); } } IntegerArgument maximumDurationArg = new IntegerArgument( argParser.maximumDurationArg.getName(), argParser.maximumDurationArg.getShortIdentifier(), argParser.maximumDurationArg.getLongIdentifier(), argParser.maximumDurationArg.isRequired(), argParser.maximumDurationArg.isMultiValued(), argParser.maximumDurationArg.needsValue(), argParser.maximumDurationArg.getValuePlaceholder(), PurgeConflictsHistoricalTask.DEFAULT_MAX_DURATION, argParser.maximumDurationArg.getPropertyName(), argParser.maximumDurationArg.getDescription()); maximumDurationArg.addValue(String.valueOf(uData.getMaximumDuration())); commandBuilder.addArgument(maximumDurationArg); } private void updateCommandBuilderWithTaskSchedule( CommandBuilder commandBuilder, TaskScheduleUserData taskSchedule) { TaskScheduleUserData.updateCommandBuilderWithTaskSchedule( commandBuilder, taskSchedule); } private void addGlobalArguments(CommandBuilder commandBuilder, ReplicationUserData uData) throws ArgumentException @@ -10064,7 +11005,7 @@ private boolean mergeRegistries(ADSContext adsCtx1, ADSContext adsCtx2) throws ReplicationCliException { PointAdder pointAdder = new PointAdder(); PointAdder pointAdder = new PointAdder(this); try { LinkedHashSet<PreferredConnection> cnx = @@ -10516,6 +11457,52 @@ { return argParser.getConnectTimeout(); } private String binDir; /** * Returns the binary/script directory. * @return the binary/script directory. */ private String getBinaryDir() { if (binDir == null) { File f = Installation.getLocal().getBinariesDirectory(); try { binDir = f.getCanonicalPath(); } catch (Throwable t) { binDir = f.getAbsolutePath(); } if (binDir.lastIndexOf(File.separatorChar) != (binDir.length() - 1)) { binDir += File.separatorChar; } } return binDir; } /** * Returns the full path of the command-line for a given script name. * @param scriptBasicName the script basic name (with no extension). * @return the full path of the command-line for a given script name. */ private String getCommandLinePath(String scriptBasicName) { String cmdLineName; if (Utilities.isWindows()) { cmdLineName = getBinaryDir()+scriptBasicName+".bat"; } else { cmdLineName = getBinaryDir()+scriptBasicName; } return cmdLineName; } } opends/src/server/org/opends/server/tools/dsreplication/ReplicationCliReturnCode.java
@@ -22,7 +22,7 @@ * CDDL HEADER END * * * Copyright 2007-2008 Sun Microsystems, Inc. * Copyright 2007-2010 Sun Microsystems, Inc. */ package org.opends.server.tools.dsreplication; @@ -181,7 +181,49 @@ /** * Error disabling replication server. */ ERROR_DISABLING_REPLICATION_SERVER(25, ERR_REPLICATION_NO_MESSAGE.get()); ERROR_DISABLING_REPLICATION_SERVER(25, ERR_REPLICATION_NO_MESSAGE.get()), /** * Error executing purge historical. */ ERROR_EXECUTING_PURGE_HISTORICAL(26, ERR_REPLICATION_NO_MESSAGE.get()), /** * The provided base DNs cannot be purged. */ HISTORICAL_CANNOT_BE_PURGED_ON_BASEDN(27, ERR_REPLICATION_NO_MESSAGE.get()), /** * Error launching purge historical. */ ERROR_LAUNCHING_PURGE_HISTORICAL(28, ERR_REPLICATION_NO_MESSAGE.get()), /** * Error loading configuration class in local purge historical. */ ERROR_LOCAL_PURGE_HISTORICAL_CLASS_LOAD(29, ERR_REPLICATION_NO_MESSAGE.get()), /** * Error starting server in local purge historical. */ ERROR_LOCAL_PURGE_HISTORICAL_SERVER_START(30, ERR_REPLICATION_NO_MESSAGE.get()), /** * Timeout error in local purge historical. */ ERROR_LOCAL_PURGE_HISTORICAL_TIMEOUT(31, ERR_REPLICATION_NO_MESSAGE.get()), /** * Generic error executing local purge historical. */ ERROR_LOCAL_PURGE_HISTORICAL_EXECUTING(32, ERR_REPLICATION_NO_MESSAGE.get()); private Message message; opends/src/server/org/opends/server/tools/dsreplication/StatusReplicationUserData.java
@@ -22,7 +22,7 @@ * CDDL HEADER END * * * Copyright 2008-2009 Sun Microsystems, Inc. * Copyright 2008-2010 Sun Microsystems, Inc. */ package org.opends.server.tools.dsreplication; @@ -33,7 +33,7 @@ * interactive mode the ReplicationCliArgumentParser is not enough. * */ public class StatusReplicationUserData extends InitializeAllReplicationUserData public class StatusReplicationUserData extends MonoServerReplicationUserData { private boolean scriptFriendly; opends/src/server/org/opends/server/tools/tasks/TaskClient.java
@@ -22,7 +22,7 @@ * CDDL HEADER END * * * Copyright 2009 Sun Microsystems, Inc. * Copyright 2009-2010 Sun Microsystems, Inc. */ package org.opends.server.tools.tasks; @@ -100,29 +100,86 @@ } /** * 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 * @throws TaskClientException if there is a problem with the task entry * Returns the ID of the task entry for a given list of task attributes. * @param taskAttributes the task attributes. * @return the ID of the task entry for a given list of task attributes. */ public synchronized TaskEntry schedule(TaskScheduleInformation information) throws LDAPException, IOException, ASN1Exception, TaskClientException public static String getTaskID(List<RawAttribute> taskAttributes) { String taskID = null; ByteString entryDN = null; boolean scheduleRecurring = false; LDAPReader reader = connection.getLDAPReader(); LDAPWriter writer = connection.getLDAPWriter(); RawAttribute recurringIDAttr = getAttribute(ATTR_RECURRING_TASK_ID, taskAttributes); if (recurringIDAttr != null) { taskID = recurringIDAttr.getValues().get(0).toString(); } else { RawAttribute taskIDAttr = getAttribute(ATTR_TASK_ID, taskAttributes); taskID = taskIDAttr.getValues().get(0).toString(); } return taskID; } private static RawAttribute getAttribute(String attrName, List<RawAttribute> taskAttributes) { for (RawAttribute attr : taskAttributes) { if (attr.getAttributeType().equalsIgnoreCase(attrName)) { return attr; } } return null; } /** * Returns the DN of the task entry for a given list of task attributes. * @param taskAttributes the task attributes. * @return the DN of the task entry for a given list of task attributes. */ public static String getTaskDN(List<RawAttribute> taskAttributes) { String entryDN = null; String taskID = getTaskID(taskAttributes); RawAttribute recurringIDAttr = getAttribute(ATTR_RECURRING_TASK_ID, taskAttributes); if (recurringIDAttr != null) { entryDN = ATTR_RECURRING_TASK_ID + "=" + taskID + "," + RECURRING_TASK_BASE_RDN + "," + DN_TASK_ROOT; } else { entryDN = ATTR_TASK_ID + "=" + taskID + "," + SCHEDULED_TASK_BASE_RDN + "," + DN_TASK_ROOT; } return entryDN; } private static boolean isScheduleRecurring( TaskScheduleInformation information) { boolean scheduleRecurring = false; if (information.getRecurringDateTime() != null) { scheduleRecurring = true; } return scheduleRecurring; } /** * This is a commodity method that returns the common attributes (those * related to scheduling) of a task entry for a given * {@link TaskScheduleInformation} object. * @param information the scheduling information. * @return the schedule attributes of the task entry. */ public static ArrayList<RawAttribute> getTaskAttributes( TaskScheduleInformation information) { String taskID = null; boolean scheduleRecurring = isScheduleRecurring(information); if (scheduleRecurring) { taskID = information.getTaskId(); @@ -130,18 +187,12 @@ taskID = information.getTaskClass().getSimpleName() + "-" + UUID.randomUUID().toString(); } entryDN = ByteString.valueOf(ATTR_RECURRING_TASK_ID + "=" + taskID + "," + RECURRING_TASK_BASE_RDN + "," + DN_TASK_ROOT); } else { // Use a formatted time/date for the ID so that is remotely useful SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmssSSS"); taskID = df.format(new Date()); entryDN = ByteString.valueOf(ATTR_TASK_ID + "=" + taskID + "," + SCHEDULED_TASK_BASE_RDN + "," + DN_TASK_ROOT); } ArrayList<Control> controls = new ArrayList<Control>(); ArrayList<RawAttribute> attributes = new ArrayList<RawAttribute>(); ArrayList<ByteString> ocValues = new ArrayList<ByteString>(3); @@ -238,6 +289,30 @@ information.addTaskAttributes(attributes); return attributes; } /** * 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 * @throws TaskClientException if there is a problem with the task entry */ public synchronized TaskEntry schedule(TaskScheduleInformation information) throws LDAPException, IOException, ASN1Exception, TaskClientException { LDAPReader reader = connection.getLDAPReader(); LDAPWriter writer = connection.getLDAPWriter(); ArrayList<Control> controls = new ArrayList<Control>(); ArrayList<RawAttribute> attributes = getTaskAttributes(information); ByteString entryDN = ByteString.valueOf(getTaskDN(attributes)); AddRequestProtocolOp addRequest = new AddRequestProtocolOp(entryDN, attributes); LDAPMessage requestMessage = @@ -271,7 +346,7 @@ LDAPResultCode.CLIENT_SIDE_LOCAL_ERROR, addResponse.getErrorMessage()); } return getTaskEntry(taskID); return getTaskEntry(getTaskID(attributes)); } /** opends/src/server/org/opends/server/tools/tasks/TaskScheduleInformation.java
@@ -22,7 +22,7 @@ * CDDL HEADER END * * * Copyright 2008 Sun Microsystems, Inc. * Copyright 2008-2010 Sun Microsystems, Inc. */ package org.opends.server.tools.tasks; @@ -65,7 +65,7 @@ * * @return class of the tasks implementation */ Class getTaskClass(); Class<?> getTaskClass(); /** opends/src/server/org/opends/server/tools/tasks/TaskScheduleInteraction.java
New file @@ -0,0 +1,477 @@ /* * 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 * * * Copyright 2010 Sun Microsystems, Inc. */ package org.opends.server.tools.tasks; import static org.opends.messages.AdminToolMessages.*; import static org.opends.messages.ToolMessages.*; import java.text.ParseException; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashSet; import java.util.List; import org.opends.messages.Message; import org.opends.quicksetup.util.ProgressMessageFormatter; import org.opends.server.admin.client.cli.TaskScheduleArgs; import org.opends.server.backends.task.FailedDependencyAction; import org.opends.server.backends.task.RecurringTask; import org.opends.server.types.DirectoryException; import org.opends.server.util.StaticUtils; import org.opends.server.util.cli.CLIException; import org.opends.server.util.cli.ConsoleApplication; import org.opends.server.util.cli.MenuBuilder; import org.opends.server.util.cli.MenuResult; /** * A class that is in charge of interacting with the user to ask about * scheduling options for a task. * <br> * It takes as argument an {@link TaskScheduleArgs} object with the arguments * provided by the user and updates the provided {@link TaskScheduleUserData} * with the information provided by the user. * */ public class TaskScheduleInteraction { private boolean headerDisplayed; private final TaskScheduleUserData uData; private final TaskScheduleArgs args; private final ConsoleApplication app; private final Message taskName; private final List<? extends TaskEntry> taskEntries; private final ProgressMessageFormatter formatter; /** * The enumeration used by the menu displayed to ask the user about the * type of scheduling (if any) to be done. * */ private enum ScheduleOption { RUN_NOW(INFO_RUN_TASK_NOW.get()), RUN_LATER(INFO_RUN_TASK_LATER.get()), SCHEDULE_TASK(INFO_SCHEDULE_TASK.get()); private Message prompt; private ScheduleOption(Message prompt) { this.prompt = prompt; } Message getPrompt() { return prompt; } /** * The default option to be proposed to the user. * @return the default option to be proposed to the user. */ public static ScheduleOption defaultValue() { return RUN_NOW; } }; /** * Default constructor. * @param uData the task schedule user data. * @param args the object with the arguments provided by the user. The code * assumes that the arguments have already been parsed. * @param app the console application object used to prompt for data. * @param formatter the formatter to be used to generated the messages. * @param taskEntries the list of task entries defined in the server. * @param taskName the name of the task to be used in the prompt messages. */ public TaskScheduleInteraction(TaskScheduleUserData uData, TaskScheduleArgs args, ConsoleApplication app, ProgressMessageFormatter formatter, List<? extends TaskEntry> taskEntries, Message taskName) { this.uData = uData; this.args = args; this.app = app; this.taskName = taskName; this.taskEntries = taskEntries; this.formatter = formatter; } /** * Executes the interaction with the user. * @throws CLIException if there is an error prompting the user. */ public void run() throws CLIException { headerDisplayed = false; runStartNowOrSchedule(); runCompletionNotification(); runErrorNotification(); runDependency(); if (!uData.getDependencyIds().isEmpty()) { runFailedDependencyAction(); } } private void runFailedDependencyAction() throws CLIException { if (args.dependencyArg.isPresent()) { uData.setFailedDependencyAction(args.getFailedDependencyAction()); } else { askForFailedDependencyAction(); } } private void askForFailedDependencyAction() throws CLIException { checkHeaderDisplay(); MenuBuilder<FailedDependencyAction> builder = new MenuBuilder<FailedDependencyAction>(app); builder.setPrompt(INFO_TASK_FAILED_DEPENDENCY_ACTION_PROMPT.get()); builder.addCancelOption(false); for (FailedDependencyAction choice : FailedDependencyAction.values()) { MenuResult<FailedDependencyAction> result = MenuResult.success(choice); builder.addNumberedOption(choice.getDisplayName(), result); if (choice.equals(FailedDependencyAction.defaultValue())) { builder.setDefault(choice.getDisplayName(), result); } } MenuResult<FailedDependencyAction> m = builder.toMenu().run(); if (m.isSuccess()) { uData.setFailedDependencyAction(m.getValue()); } else { throw new CLIException(Message.EMPTY); } } private void runDependency() throws CLIException { if (args.dependencyArg.isPresent()) { uData.setDependencyIds(args.getDependencyIds()); } else if (!taskEntries.isEmpty()) { askForDependency(); } } private void askForDependency() throws CLIException { checkHeaderDisplay(); boolean hasDependencies = app.confirmAction(INFO_TASK_HAS_DEPENDENCIES_PROMPT.get(), false); if (hasDependencies) { printAvailableDependencyTaskMessage(); HashSet<String> dependencies = new HashSet<String>(); while (true) { String dependencyID = app.readLineOfInput(INFO_TASK_DEPENDENCIES_PROMPT.get()); if (dependencyID != null && !dependencyID.isEmpty()) { if (isTaskIDDefined(dependencyID)) { dependencies.add(dependencyID); } else { printTaskIDNotDefinedMessage(dependencyID); } } else { break; } } uData.setDependencyIds(new ArrayList<String>(dependencies)); } else { List<String> empty = Collections.emptyList(); uData.setDependencyIds(empty); } } private void printAvailableDependencyTaskMessage() { StringBuilder sb = new StringBuilder(); String separator = formatter.getLineBreak().toString() + formatter.getTab().toString(); for (TaskEntry entry : taskEntries) { sb.append(separator); sb.append(entry.getId()); } app.printlnProgress(); app.printProgress(INFO_AVAILABLE_DEFINED_TASKS.get(sb.toString())); app.printlnProgress(); app.printlnProgress(); } private void printTaskIDNotDefinedMessage(String dependencyID) { app.println(); app.println(ERR_DEPENDENCY_TASK_NOT_DEFINED.get(dependencyID)); } private boolean isTaskIDDefined(String dependencyID) { boolean taskIDDefined = false; for (TaskEntry entry : taskEntries) { if (dependencyID.equalsIgnoreCase(entry.getId())) { taskIDDefined = true; break; } } return taskIDDefined; } private void runErrorNotification() throws CLIException { if (args.errorNotificationArg.isPresent()) { uData.setNotifyUponErrorEmailAddresses( args.getNotifyUponErrorEmailAddresses()); } else { askForErrorNotification(); } } private void askForErrorNotification() throws CLIException { List<String> addresses = askForEmailNotification(INFO_HAS_ERROR_NOTIFICATION_PROMPT.get(), INFO_ERROR_NOTIFICATION_PROMPT.get()); uData.setNotifyUponErrorEmailAddresses(addresses); } private List<String> askForEmailNotification(Message hasNotificationPrompt, Message emailAddressPrompt) throws CLIException { checkHeaderDisplay(); List<String> addresses = new ArrayList<String>(); boolean hasNotification = app.confirmAction(hasNotificationPrompt, false); if (hasNotification) { HashSet<String> set = new HashSet<String>(); while (true) { String address = app.readLineOfInput(emailAddressPrompt); if (address != null && !address.isEmpty()) { if (!StaticUtils.isEmailAddress(address)) { app.println(ERR_INVALID_EMAIL_ADDRESS.get(address)); } else { set.add(address); } } else { break; } } addresses.addAll(set); } return addresses; } private void runCompletionNotification() throws CLIException { if (args.completionNotificationArg.isPresent()) { uData.setNotifyUponCompletionEmailAddresses( args.getNotifyUponCompletionEmailAddresses()); } else { askForCompletionNotification(); } } private void askForCompletionNotification() throws CLIException { List<String> addresses = askForEmailNotification(INFO_HAS_COMPLETION_NOTIFICATION_PROMPT.get(), INFO_COMPLETION_NOTIFICATION_PROMPT.get()); uData.setNotifyUponCompletionEmailAddresses(addresses); } private void runStartNowOrSchedule() throws CLIException { if (args.startArg.isPresent()) { uData.setStartDate(args.getStartDateTime()); uData.setStartNow(args.isStartNow()); } if (args.recurringArg.isPresent()) { uData.setRecurringDateTime(args.getRecurringDateTime()); uData.setStartNow(false); } if (!args.startArg.isPresent() && !args.recurringArg.isPresent()) { askToStartNowOrSchedule(); } } private void askToStartNowOrSchedule() throws CLIException { checkHeaderDisplay(); MenuBuilder<ScheduleOption> builder = new MenuBuilder<ScheduleOption>(app); builder.setPrompt(INFO_TASK_SCHEDULE_PROMPT.get(taskName)); builder.addCancelOption(false); for (ScheduleOption choice : ScheduleOption.values()) { MenuResult<ScheduleOption> result = MenuResult.success(choice); if (choice == ScheduleOption.defaultValue()) { builder.setDefault(choice.getPrompt(), result); } builder.addNumberedOption(choice.getPrompt(), result); } MenuResult<ScheduleOption> m = builder.toMenu().run(); if (m.isSuccess()) { switch (m.getValue()) { case RUN_NOW: uData.setStartNow(true); break; case RUN_LATER: uData.setStartNow(false); askForStartDate(); break; case SCHEDULE_TASK: uData.setStartNow(false); askForTaskSchedule(); break; } } else { throw new CLIException(Message.EMPTY); } } private void askForStartDate() throws CLIException { checkHeaderDisplay(); Date startDate = null; while (startDate == null) { String sDate = app.readInput(INFO_TASK_START_DATE_PROMPT.get(), null); try { startDate = StaticUtils.parseDateTimeString(sDate); // Check that the provided date is not previous to the current date. Date currentDate = new Date(System.currentTimeMillis()); if (currentDate.after(startDate)) { app.printProgress(ERR_START_DATETIME_ALREADY_PASSED.get(sDate)); app.printlnProgress(); app.printlnProgress(); startDate = null; } } catch (ParseException pe) { app.println(ERR_START_DATETIME_FORMAT.get()); app.println(); } } uData.setStartDate(startDate); } private void askForTaskSchedule() throws CLIException { checkHeaderDisplay(); String schedule = null; while (schedule == null) { schedule = app.readInput(INFO_TASK_RECURRING_SCHEDULE_PROMPT.get(), null); try { RecurringTask.parseTaskTab(schedule); app.printlnProgress(); } catch (DirectoryException de) { schedule = null; app.println(ERR_RECURRING_SCHEDULE_FORMAT_ERROR.get( de.getMessageObject())); app.println(); } } uData.setRecurringDateTime(schedule); } private void checkHeaderDisplay() { if (!headerDisplayed) { app.printlnProgress(); app.printProgress(INFO_TASK_SCHEDULE_PROMPT_HEADER.get()); app.printlnProgress(); headerDisplayed = true; } app.printlnProgress(); } } opends/src/server/org/opends/server/tools/tasks/TaskScheduleUserData.java
New file @@ -0,0 +1,303 @@ /* * 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 * * * Copyright 2010 Sun Microsystems, Inc. */ package org.opends.server.tools.tasks; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.List; import org.opends.server.admin.client.cli.TaskScheduleArgs; import org.opends.server.backends.task.FailedDependencyAction; import org.opends.server.util.StaticUtils; import org.opends.server.util.args.ArgumentException; import org.opends.server.util.args.StringArgument; import org.opends.server.util.cli.CommandBuilder; /** * A generic data structure that contains the data that the user provided to * schedule a task. * <br> * The main difference with {@link TaskScheduleInformation} is that this class * is completely agnostic of the execution. */ public class TaskScheduleUserData { private boolean startNow; private Date startDate; private String recurringDateTime; private final List<String> dependencyIds = new ArrayList<String>(); private FailedDependencyAction failedDependencyAction; private final List<String> notifyUponCompletionEmailAddresses = new ArrayList<String>(); private final List<String> notifyUponErrorEmailAddresses = new ArrayList<String>(); /** * Whether the arguments provided by the user, indicate that the task should * be executed immediately. * @return {@code true} if the task must be executed immediately and * {@code false} otherwise. */ public boolean isStartNow() { return startNow; } /** * Sets whether the arguments provided by the user, indicate that the task * should be executed immediately. * @param startNow {@code true} if the task must be executed immediately and * {@code false} otherwise. */ public void setStartNow(boolean startNow) { this.startNow = startNow; } /** * Gets the date at which this task should be scheduled to start. * * @return date/time at which the task should be scheduled */ public Date getStartDate() { return startDate; } /** * Sets the date at which this task should be scheduled to start. * * @param startDate the date/time at which the task should be scheduled */ public void setStartDate(Date startDate) { this.startDate = startDate; } /** * Gets the date/time pattern for recurring task schedule. * * @return recurring date/time pattern at which the task * should be scheduled. */ public String getRecurringDateTime() { return recurringDateTime; } /** * Sets the date/time pattern for recurring task schedule. * * @param recurringDateTime recurring date/time pattern at which the task * should be scheduled. */ public void setRecurringDateTime(String recurringDateTime) { this.recurringDateTime = recurringDateTime; } /** * Gets a list of task IDs upon which this task is dependent. * * @return list of task IDs */ public List<String> getDependencyIds() { return dependencyIds; } /** * Sets the list of task IDs upon which this task is dependent. * * @param dependencyIds list of task IDs */ public void setDependencyIds(List<String> dependencyIds) { this.dependencyIds.clear(); this.dependencyIds.addAll(dependencyIds); } /** * Gets the action to take should one of the dependent task fail. * * @return action to take */ public FailedDependencyAction getFailedDependencyAction() { return failedDependencyAction; } /** * Sets the action to take should one of the dependent task fail. * * @param failedDependencyAction the action to take */ public void setFailedDependencyAction( FailedDependencyAction failedDependencyAction) { this.failedDependencyAction = failedDependencyAction; } /** * Gets a list of email address to which an email will be sent when this * task completes. * * @return list of email addresses */ public List<String> getNotifyUponCompletionEmailAddresses() { return notifyUponCompletionEmailAddresses; } /** * Sets the list of email address to which an email will be sent when this * task completes. * * @param notifyUponCompletionEmailAddresses the list of email addresses */ public void setNotifyUponCompletionEmailAddresses( List<String> notifyUponCompletionEmailAddresses) { this.notifyUponCompletionEmailAddresses.clear(); this.notifyUponCompletionEmailAddresses.addAll( notifyUponCompletionEmailAddresses); } /** * Gets the list of email address to which an email will be sent if this * task encounters an error during execution. * * @return list of email addresses */ public List<String> getNotifyUponErrorEmailAddresses() { return notifyUponErrorEmailAddresses; } /** * Sets the list of email address to which an email will be sent if this * task encounters an error during execution. * * @param notifyUponErrorEmailAddresses the list of email addresses */ public void setNotifyUponErrorEmailAddresses( List<String> notifyUponErrorEmailAddresses) { this.notifyUponErrorEmailAddresses.clear(); this.notifyUponErrorEmailAddresses.addAll(notifyUponErrorEmailAddresses); } /** * An static utility method that can be used to update the object used to * display the equivalent command-line with the contents of a given * task schedule object. * @param commandBuilder the command builder. * @param taskSchedule the task schedule. */ public static void updateCommandBuilderWithTaskSchedule( CommandBuilder commandBuilder, TaskScheduleUserData taskSchedule) { TaskScheduleArgs argsToClone = new TaskScheduleArgs(); String sDate = null; String recurringDateTime = null; if (!taskSchedule.isStartNow()) { Date date = taskSchedule.getStartDate(); if (date != null) { sDate = StaticUtils.formatDateTimeString(date); } recurringDateTime = taskSchedule.getRecurringDateTime(); } String sFailedDependencyAction = null; FailedDependencyAction fAction = taskSchedule.getFailedDependencyAction(); if (fAction != null) { sFailedDependencyAction = fAction.name(); } String[] sValues = {sDate, recurringDateTime, sFailedDependencyAction}; StringArgument[] args = {argsToClone.startArg, argsToClone.recurringArg, argsToClone.failedDependencyActionArg}; for (int i=0; i<sValues.length; i++) { if (sValues[i] != null) { commandBuilder.addArgument(getArgument(args[i], Collections.singleton(sValues[i]))); } } List<?>[] values = {taskSchedule.getDependencyIds(), taskSchedule.getNotifyUponCompletionEmailAddresses(), taskSchedule.getNotifyUponErrorEmailAddresses()}; args = new StringArgument[]{argsToClone.dependencyArg, argsToClone.completionNotificationArg, argsToClone.errorNotificationArg}; for (int i=0; i<values.length; i++) { if (!values[i].isEmpty()) { commandBuilder.addArgument(getArgument(args[i], values[i])); } } } private static StringArgument getArgument( StringArgument argToClone, Collection<?> values) { StringArgument arg; try { arg = new StringArgument(argToClone.getName(), argToClone.getShortIdentifier(), argToClone.getLongIdentifier(), argToClone.isRequired(), argToClone.isMultiValued(), argToClone.needsValue(), argToClone.getValuePlaceholder(), argToClone.getDefaultValue(), argToClone.getPropertyName(), argToClone.getDescription()); } catch (ArgumentException e) { // This is a bug. throw new RuntimeException("Unexpected error: "+e, e); } for (Object v : values) { arg.addValue(String.valueOf(v)); } return arg; } } opends/src/server/org/opends/server/tools/tasks/TaskTool.java
@@ -27,6 +27,7 @@ package org.opends.server.tools.tasks; import org.opends.server.util.args.Argument; import org.opends.server.util.args.BooleanArgument; import org.opends.server.util.args.LDAPConnectionArgumentParser; import org.opends.server.util.args.ArgumentException; @@ -37,7 +38,6 @@ import static org.opends.server.util.StaticUtils.wrapText; import static org.opends.server.util.StaticUtils.getExceptionMessage; import static org.opends.server.util.ServerConstants.MAX_LINE_WIDTH; import org.opends.server.util.StaticUtils; import org.opends.server.protocols.asn1.ASN1Exception; import org.opends.server.tools.LDAPConnection; import org.opends.server.tools.LDAPConnectionException; @@ -46,6 +46,7 @@ import org.opends.server.types.LDAPException; import org.opends.server.types.OpenDsException; import org.opends.server.core.DirectoryServer; import org.opends.server.admin.client.cli.TaskScheduleArgs; import org.opends.server.backends.task.TaskState; import org.opends.server.backends.task.FailedDependencyAction; import org.opends.messages.Message; @@ -53,14 +54,10 @@ import static org.opends.messages.TaskMessages.*; import java.io.PrintStream; import java.text.ParseException; import java.util.Date; import java.util.Set; import java.util.HashSet; import java.util.List; import java.util.LinkedList; import java.util.EnumSet; import java.util.Collections; import java.io.IOException; /** @@ -75,9 +72,17 @@ * this operation to run immediately as a task as opposed to running * the operation in the local VM. */ public static final String NOW = "0"; public static final String NOW = TaskScheduleArgs.NOW; /** * The error code used by the mixed-script to know if the java * arguments for the off-line mode must be used. */ private static final int RUN_OFFLINE = 51; /** * The error code used by the mixed-script to know if the java * arguments for the on-line mode must be used. */ private static final int RUN_ONLINE = 52; // Number of milliseconds this utility will wait before reloading @@ -86,25 +91,9 @@ private LDAPConnectionArgumentParser argParser; // Argument for describing the task's start time private StringArgument startArg; private TaskScheduleArgs taskScheduleArgs; // Argument to indicate a recurring task private StringArgument recurringArg; // Argument for specifying completion notifications private StringArgument completionNotificationArg; // Argument for specifying error notifications private StringArgument errorNotificationArg; // Argument for specifying dependency private StringArgument dependencyArg; // Argument for specifying a failed dependency action private StringArgument failedDependencyActionArg; // Argument used to know whether we must test if we must run in offline // Argument used to know whether we must test if we must run in off-line // mode. private BooleanArgument testIfOfflineArg; @@ -160,66 +149,17 @@ argParser.addArgument(noPropertiesFileArgument); argParser.setNoPropertiesFileArgument(noPropertiesFileArgument); startArg = new StringArgument( OPTION_LONG_START_DATETIME, OPTION_SHORT_START_DATETIME, OPTION_LONG_START_DATETIME, false, false, true, INFO_START_DATETIME_PLACEHOLDER.get(), null, null, INFO_DESCRIPTION_START_DATETIME.get()); argParser.addArgument(startArg, taskGroup); taskScheduleArgs = new TaskScheduleArgs(); recurringArg = new StringArgument( OPTION_LONG_RECURRING_TASK, OPTION_SHORT_RECURRING_TASK, OPTION_LONG_RECURRING_TASK, false, false, true, INFO_RECURRING_TASK_PLACEHOLDER.get(), null, null, INFO_DESCRIPTION_RECURRING_TASK.get()); argParser.addArgument(recurringArg, taskGroup); for (Argument arg : taskScheduleArgs.getArguments()) { argParser.addArgument(arg, taskGroup); } completionNotificationArg = new StringArgument( OPTION_LONG_COMPLETION_NOTIFICATION_EMAIL, OPTION_SHORT_COMPLETION_NOTIFICATION_EMAIL, OPTION_LONG_COMPLETION_NOTIFICATION_EMAIL, false, true, true, INFO_EMAIL_ADDRESS_PLACEHOLDER.get(), null, null, INFO_DESCRIPTION_TASK_COMPLETION_NOTIFICATION.get()); argParser.addArgument(completionNotificationArg, taskGroup); errorNotificationArg = new StringArgument( OPTION_LONG_ERROR_NOTIFICATION_EMAIL, OPTION_SHORT_ERROR_NOTIFICATION_EMAIL, OPTION_LONG_ERROR_NOTIFICATION_EMAIL, false, true, true, INFO_EMAIL_ADDRESS_PLACEHOLDER.get(), null, null, INFO_DESCRIPTION_TASK_ERROR_NOTIFICATION.get()); argParser.addArgument(errorNotificationArg, taskGroup); dependencyArg = new StringArgument( OPTION_LONG_DEPENDENCY, OPTION_SHORT_DEPENDENCY, OPTION_LONG_DEPENDENCY, false, true, true, INFO_TASK_ID_PLACEHOLDER.get(), null, null, INFO_DESCRIPTION_TASK_DEPENDENCY_ID.get()); argParser.addArgument(dependencyArg, taskGroup); Set<FailedDependencyAction> fdaValSet = EnumSet.allOf(FailedDependencyAction.class); failedDependencyActionArg = new StringArgument( OPTION_LONG_FAILED_DEPENDENCY_ACTION, OPTION_SHORT_FAILED_DEPENDENCY_ACTION, OPTION_LONG_FAILED_DEPENDENCY_ACTION, false, true, true, INFO_ACTION_PLACEHOLDER.get(), null, null, INFO_DESCRIPTION_TASK_FAILED_DEPENDENCY_ACTION.get( StaticUtils.collectionToString(fdaValSet, ","), FailedDependencyAction.defaultValue().name())); argParser.addArgument(failedDependencyActionArg, taskGroup); testIfOfflineArg = new BooleanArgument( "testIfOffline", null, "testIfOffline", INFO_DESCRIPTION_TEST_IF_OFFLINE.get()); testIfOfflineArg = new BooleanArgument("testIfOffline", null, "testIfOffline", INFO_DESCRIPTION_TEST_IF_OFFLINE.get()); testIfOfflineArg.setHidden(true); argParser.addArgument(testIfOfflineArg); } catch (ArgumentException e) { // should never happen } @@ -238,81 +178,13 @@ */ protected void validateTaskArgs() throws ArgumentException, CLIException { if ((startArg.isPresent() || recurringArg.isPresent()) && !processAsTask()) if (processAsTask()) { throw new ArgumentException( ERR_TASK_TOOL_NO_VALID_LDAP_OPTIONS.get()); taskScheduleArgs.validateArgs(); } if (startArg.isPresent() && !NOW.equals(startArg.getValue())) { try { Date date = StaticUtils.parseDateTimeString(startArg.getValue()); // Check that the provided date is not previous to the current date. Date currentDate = new Date(System.currentTimeMillis()); if (currentDate.after(date)) { throw new CLIException(ERR_START_DATETIME_ALREADY_PASSED.get( startArg.getValue())); } } catch (ParseException pe) { throw new ArgumentException(ERR_START_DATETIME_FORMAT.get()); } } if (!processAsTask() && completionNotificationArg.isPresent()) { throw new ArgumentException(ERR_TASKTOOL_OPTIONS_FOR_TASK_ONLY.get( completionNotificationArg.getLongIdentifier())); } if (!processAsTask() && errorNotificationArg.isPresent()) { throw new ArgumentException(ERR_TASKTOOL_OPTIONS_FOR_TASK_ONLY.get( errorNotificationArg.getLongIdentifier())); } if (!processAsTask() && dependencyArg.isPresent()) { throw new ArgumentException(ERR_TASKTOOL_OPTIONS_FOR_TASK_ONLY.get( dependencyArg.getLongIdentifier())); } if (!processAsTask() && failedDependencyActionArg.isPresent()) { throw new ArgumentException(ERR_TASKTOOL_OPTIONS_FOR_TASK_ONLY.get( failedDependencyActionArg.getLongIdentifier())); } if (completionNotificationArg.isPresent()) { LinkedList<String> addrs = completionNotificationArg.getValues(); for (String addr : addrs) { if (!StaticUtils.isEmailAddress(addr)) { throw new ArgumentException(ERR_TASKTOOL_INVALID_EMAIL_ADDRESS.get( addr, completionNotificationArg.getLongIdentifier())); } } } if (errorNotificationArg.isPresent()) { LinkedList<String> addrs = errorNotificationArg.getValues(); for (String addr : addrs) { if (!StaticUtils.isEmailAddress(addr)) { throw new ArgumentException(ERR_TASKTOOL_INVALID_EMAIL_ADDRESS.get( addr, errorNotificationArg.getLongIdentifier())); } } } if (failedDependencyActionArg.isPresent()) { if (!dependencyArg.isPresent()) { throw new ArgumentException(ERR_TASKTOOL_FDA_WITH_NO_DEPENDENCY.get()); } String fda = failedDependencyActionArg.getValue(); if (null == FailedDependencyAction.fromString(fda)) { Set<FailedDependencyAction> fdaValSet = EnumSet.allOf(FailedDependencyAction.class); throw new ArgumentException(ERR_TASKTOOL_INVALID_FDA.get(fda, StaticUtils.collectionToString(fdaValSet, ","))); } else { taskScheduleArgs.validateArgsIfOffline(); } } @@ -320,79 +192,42 @@ * {@inheritDoc} */ public Date getStartDateTime() { Date start = null; // If the start time arg is present parse its value if (startArg != null && startArg.isPresent()) { if (NOW.equals(startArg.getValue())) { start = new Date(); } else { try { start = StaticUtils.parseDateTimeString(startArg.getValue()); } catch (ParseException pe) { // ignore; validated in validateTaskArgs() } } } return start; return taskScheduleArgs.getStartDateTime(); } /** * {@inheritDoc} */ public String getRecurringDateTime() { String pattern = null; // If the recurring task arg is present parse its value if (recurringArg != null && recurringArg.isPresent()) { pattern = recurringArg.getValue(); } return pattern; return taskScheduleArgs.getRecurringDateTime(); } /** * {@inheritDoc} */ public List<String> getDependencyIds() { if (dependencyArg.isPresent()) { return dependencyArg.getValues(); } else { return Collections.emptyList(); } return taskScheduleArgs.getDependencyIds(); } /** * {@inheritDoc} */ public FailedDependencyAction getFailedDependencyAction() { FailedDependencyAction fda = null; if (failedDependencyActionArg.isPresent()) { String fdaString = failedDependencyActionArg.getValue(); fda = FailedDependencyAction.fromString(fdaString); } return fda; return taskScheduleArgs.getFailedDependencyAction(); } /** * {@inheritDoc} */ public List<String> getNotifyUponCompletionEmailAddresses() { if (completionNotificationArg.isPresent()) { return completionNotificationArg.getValues(); } else { return Collections.emptyList(); } return taskScheduleArgs.getNotifyUponCompletionEmailAddresses(); } /** * {@inheritDoc} */ public List<String> getNotifyUponErrorEmailAddresses() { if (errorNotificationArg.isPresent()) { return errorNotificationArg.getValues(); } else { return Collections.emptyList(); } return taskScheduleArgs.getNotifyUponErrorEmailAddresses(); } /** @@ -467,7 +302,7 @@ taskEntry.getScheduledStartTime()), MAX_LINE_WIDTH)); } if (!startArg.isPresent()) { if (!taskScheduleArgs.startArg.isPresent()) { // Poll the task printing log messages until finished String taskId = taskEntry.getId(); @@ -562,22 +397,6 @@ } /** * Indicates whether we must return if the command must be run in offline * mode. * @return <CODE>true</CODE> if we must return if the command must be run in * offline mode and <CODE>false</CODE> otherwise. */ private boolean testIfOffline() { boolean returnValue = false; if (testIfOfflineArg != null) { returnValue = testIfOfflineArg.isPresent(); } return returnValue; } /** * Returns {@code true} if the provided exception was caused by trying to * connect to the wrong port and {@code false} otherwise. * @param t the exception to be analyzed. @@ -603,4 +422,21 @@ } return isWrongPortException; } /** * Indicates whether we must return if the command must be run in off-line * mode. * @return <CODE>true</CODE> if we must return if the command must be run in * off-line mode and <CODE>false</CODE> otherwise. */ public boolean testIfOffline() { boolean returnValue = false; if (testIfOfflineArg != null) { returnValue = testIfOfflineArg.isPresent(); } return returnValue; } } opends/src/server/org/opends/server/util/StaticUtils.java
@@ -4121,11 +4121,23 @@ { if (timeStr.endsWith("Z")) { SimpleDateFormat dateFormat = try { SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_GENERALIZED_TIME); dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); dateFormat.setLenient(true); dateTime = dateFormat.parse(timeStr); dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); dateFormat.setLenient(true); dateTime = dateFormat.parse(timeStr); } catch (ParseException pe) { // Best effort: try with GMT time. SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_GMT_TIME); dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); dateFormat.setLenient(true); dateTime = dateFormat.parse(timeStr); } } else { opends/src/server/org/opends/server/util/cli/ConsoleApplication.java
@@ -62,9 +62,6 @@ import org.opends.admin.ads.util.ConnectionUtils; import org.opends.admin.ads.util.OpendsCertificateException; import org.opends.messages.Message; import org.opends.messages.MessageBuilder; import org.opends.quicksetup.util.PlainTextProgressMessageFormatter; import org.opends.quicksetup.util.ProgressMessageFormatter; import org.opends.quicksetup.util.Utils; import org.opends.server.protocols.ldap.LDAPResultCode; import org.opends.server.tools.ClientException; @@ -1116,120 +1113,6 @@ return pwd; } /** * The default period time used to write points in the output. */ protected static final long DEFAULT_PERIOD_TIME = 3000; /** * Class used to add points periodically to the end of the output. * */ protected class PointAdder implements Runnable { private Thread t; private boolean stopPointAdder; private boolean pointAdderStopped; private long periodTime = DEFAULT_PERIOD_TIME; private boolean isError; private ProgressMessageFormatter formatter; /** * Default constructor. * Creates a PointAdder that writes to the standard output with the default * period time. */ public PointAdder() { this(DEFAULT_PERIOD_TIME, false, new PlainTextProgressMessageFormatter()); } /** * Default constructor. * @param periodTime the time between printing two points. * @param isError whether the points must be printed in error stream * or output stream. * @param formatter the text formatter. */ public PointAdder(long periodTime, boolean isError, ProgressMessageFormatter formatter) { this.periodTime = periodTime; this.isError = isError; this.formatter = formatter; } /** * Starts the PointAdder: points are added at the end of the logs * periodically. */ public void start() { MessageBuilder mb = new MessageBuilder(); mb.append(formatter.getSpace()); for (int i=0; i< 5; i++) { mb.append(formatter.getFormattedPoint()); } if (isError) { print(mb.toMessage()); } else { printProgress(mb.toMessage()); } t = new Thread(this); t.start(); } /** * Stops the PointAdder: points are no longer added at the end of the logs * periodically. */ public synchronized void stop() { stopPointAdder = true; while (!pointAdderStopped) { try { t.interrupt(); // To allow the thread to set the boolean. Thread.sleep(100); } catch (Throwable t) { } } } /** * {@inheritDoc} */ public void run() { while (!stopPointAdder) { try { Thread.sleep(periodTime); if (isError) { print(formatter.getFormattedPoint()); } else { printProgress(formatter.getFormattedPoint()); } } catch (Throwable t) { } } pointAdderStopped = true; } } private OpendsCertificateException getCertificateRootException(Throwable t) { OpendsCertificateException oce = null; @@ -1243,4 +1126,89 @@ } return oce; } /** * Commodity method used to repeatidly ask the user to provide an * integer value. * @param prompt the prompt message. * @param defaultValue the default value to be proposed to the user. * @param logger the logger where the errors will be written. * @return the value provided by the user. */ protected int askInteger(Message prompt, int defaultValue, Logger logger) { int newInt = -1; while (newInt == -1) { try { newInt = readInteger(prompt, defaultValue); } catch (CLIException ce) { newInt = -1; logger.log(Level.WARNING, "Error reading input: "+ce, ce); } } return newInt; } /** * Interactively retrieves an integer value from the console. * * @param prompt * The message prompt. * @param defaultValue * The default value. * @return Returns the value. * @throws CLIException * If the value could not be retrieved for some reason. */ public final int readInteger(Message prompt, final int defaultValue) throws CLIException { ValidationCallback<Integer> callback = new ValidationCallback<Integer>() { public Integer validate(ConsoleApplication app, String input) throws CLIException { String ninput = input.trim(); if (ninput.length() == 0) { return defaultValue; } else { try { int i = Integer.parseInt(ninput); if (i < 1) { throw new NumberFormatException(); } return i; } catch (NumberFormatException e) { // Try again... app.println(); app.println(ERR_LDAP_CONN_BAD_INTEGER.get(ninput)); app.println(); return null; } } } }; if (defaultValue != -1) { prompt = INFO_PROMPT_SINGLE_DEFAULT.get(prompt.toString(), String.valueOf(defaultValue)); } return readValidatedInput(prompt, callback, CONFIRMATION_MAX_TRIES); } } opends/src/server/org/opends/server/util/cli/PointAdder.java
New file @@ -0,0 +1,153 @@ /* * 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 * * * Copyright 2010 Sun Microsystems, Inc. */ package org.opends.server.util.cli; import org.opends.messages.MessageBuilder; import org.opends.quicksetup.util.PlainTextProgressMessageFormatter; import org.opends.quicksetup.util.ProgressMessageFormatter; /** * Class used to add points periodically to the end of the output. * */ public class PointAdder implements Runnable { private final ConsoleApplication app; private Thread t; private boolean stopPointAdder; private boolean pointAdderStopped; private long periodTime = DEFAULT_PERIOD_TIME; private final boolean isError; private final ProgressMessageFormatter formatter; /** * The default period time used to write points in the output. */ public static final long DEFAULT_PERIOD_TIME = 3000; /** * Default constructor. * @param app the console application to be used. * Creates a PointAdder that writes to the standard output with the default * period time. */ public PointAdder(ConsoleApplication app) { this(app, DEFAULT_PERIOD_TIME, false, new PlainTextProgressMessageFormatter()); } /** * Default constructor. * @param app the console application to be used. * @param periodTime the time between printing two points. * @param isError whether the points must be printed in error stream * or output stream. * @param formatter the text formatter. */ public PointAdder(ConsoleApplication app, long periodTime, boolean isError, ProgressMessageFormatter formatter) { this.app = app; this.periodTime = periodTime; this.isError = isError; this.formatter = formatter; } /** * Starts the PointAdder: points are added at the end of the logs * periodically. */ public void start() { MessageBuilder mb = new MessageBuilder(); mb.append(formatter.getSpace()); for (int i=0; i< 5; i++) { mb.append(formatter.getFormattedPoint()); } if (isError) { app.print(mb.toMessage()); } else { app.printProgress(mb.toMessage()); } t = new Thread(this); t.start(); } /** * Stops the PointAdder: points are no longer added at the end of the logs * periodically. */ public synchronized void stop() { stopPointAdder = true; while (!pointAdderStopped) { try { t.interrupt(); // To allow the thread to set the boolean. Thread.sleep(100); } catch (Throwable t) { } } } /** * {@inheritDoc} */ public void run() { while (!stopPointAdder) { try { Thread.sleep(periodTime); if (isError) { app.print(formatter.getFormattedPoint()); } else { app.printProgress(formatter.getFormattedPoint()); } } catch (Throwable t) { } } pointAdderStopped = true; } }