/* * 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 legal-notices/CDDLv1_0.txt * or http://forgerock.org/license/CDDLv1.0.html. * 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 legal-notices/CDDLv1_0.txt. * 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. * Portions Copyright 2014-2015 ForgeRock AS */ 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.forgerock.i18n.LocalizableMessage; import org.opends.quicksetup.util.PlainTextProgressMessageFormatter; 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 com.forgerock.opendj.cli.ClientException; import com.forgerock.opendj.cli.ReturnCode; import com.forgerock.opendj.cli.ConsoleApplication; import com.forgerock.opendj.cli.MenuBuilder; import com.forgerock.opendj.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 LocalizableMessage taskName; private List<? extends TaskEntry> taskEntries = Collections.emptyList(); private ProgressMessageFormatter formatter = new PlainTextProgressMessageFormatter(); /** * 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 LocalizableMessage prompt; private ScheduleOption(LocalizableMessage prompt) { this.prompt = prompt; } LocalizableMessage 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 taskName the name of the task to be used in the prompt messages. */ public TaskScheduleInteraction(TaskScheduleUserData uData, TaskScheduleArgs args, ConsoleApplication app, LocalizableMessage taskName) { this.uData = uData; this.args = args; this.app = app; this.taskName = taskName; } /** * Executes the interaction with the user. * @throws ClientException if there is an error prompting the user. */ public void run() throws ClientException { headerDisplayed = false; runStartNowOrSchedule(); runCompletionNotification(); runErrorNotification(); runDependency(); if (!uData.getDependencyIds().isEmpty()) { runFailedDependencyAction(); } } /** * Returns the task entries that are defined in the server. These are * used to prompt the user about the task dependencies. * @return the task entries that are defined in the server. */ public List<? extends TaskEntry> getTaskEntries() { return taskEntries; } /** * Sets the task entries that are defined in the server. These are * used to prompt the user about the task dependencies. If no task entries * are provided, the user will not be prompted for task dependencies. * @param taskEntries the task entries that are defined in the server. */ public void setTaskEntries(List<? extends TaskEntry> taskEntries) { this.taskEntries = taskEntries; } /** * Returns the formatter that is used to generate messages. * @return the formatter that is used to generate messages. */ public ProgressMessageFormatter getFormatter() { return formatter; } /** * Sets the formatter that is used to generate messages. * @param formatter the formatter that is used to generate messages. */ public void setFormatter(ProgressMessageFormatter formatter) { this.formatter = formatter; } private void runFailedDependencyAction() throws ClientException { if (args.dependencyArg.isPresent()) { uData.setFailedDependencyAction(args.getFailedDependencyAction()); } else { askForFailedDependencyAction(); } } private void askForFailedDependencyAction() throws ClientException { checkHeaderDisplay(); MenuBuilder<FailedDependencyAction> builder = new MenuBuilder<>(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 ClientException(ReturnCode.ERROR_UNEXPECTED, LocalizableMessage.EMPTY); } } private void runDependency() throws ClientException { if (args.dependencyArg.isPresent()) { uData.setDependencyIds(args.getDependencyIds()); } else if (!taskEntries.isEmpty()) { askForDependency(); } } private void askForDependency() throws ClientException { checkHeaderDisplay(); boolean hasDependencies = app.confirmAction(INFO_TASK_HAS_DEPENDENCIES_PROMPT.get(), false); if (hasDependencies) { printAvailableDependencyTaskMessage(); HashSet<String> dependencies = new HashSet<>(); 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(); for (TaskEntry entry : taskEntries) { sb.append(separator); sb.append(entry.getId()); } app.println(); app.print(INFO_AVAILABLE_DEFINED_TASKS.get(sb)); app.println(); app.println(); } 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 ClientException { if (args.errorNotificationArg.isPresent()) { uData.setNotifyUponErrorEmailAddresses( args.getNotifyUponErrorEmailAddresses()); } else { askForErrorNotification(); } } private void askForErrorNotification() throws ClientException { List<String> addresses = askForEmailNotification(INFO_HAS_ERROR_NOTIFICATION_PROMPT.get(), INFO_ERROR_NOTIFICATION_PROMPT.get()); uData.setNotifyUponErrorEmailAddresses(addresses); } private List<String> askForEmailNotification(LocalizableMessage hasNotificationPrompt, LocalizableMessage emailAddressPrompt) throws ClientException { checkHeaderDisplay(); List<String> addresses = new ArrayList<>(); boolean hasNotification = app.confirmAction(hasNotificationPrompt, false); if (hasNotification) { HashSet<String> set = new HashSet<>(); while (true) { String address = app.readLineOfInput(emailAddressPrompt); if (address == null || address.isEmpty()) { break; } if (!StaticUtils.isEmailAddress(address)) { app.println(ERR_INVALID_EMAIL_ADDRESS.get(address)); } else { set.add(address); } } addresses.addAll(set); } return addresses; } private void runCompletionNotification() throws ClientException { if (args.completionNotificationArg.isPresent()) { uData.setNotifyUponCompletionEmailAddresses( args.getNotifyUponCompletionEmailAddresses()); } else { askForCompletionNotification(); } } private void askForCompletionNotification() throws ClientException { List<String> addresses = askForEmailNotification(INFO_HAS_COMPLETION_NOTIFICATION_PROMPT.get(), INFO_COMPLETION_NOTIFICATION_PROMPT.get()); uData.setNotifyUponCompletionEmailAddresses(addresses); } private void runStartNowOrSchedule() throws ClientException { 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 ClientException { checkHeaderDisplay(); MenuBuilder<ScheduleOption> builder = new MenuBuilder<>(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 ClientException(ReturnCode.ERROR_UNEXPECTED, LocalizableMessage.EMPTY); } } private void askForStartDate() throws ClientException { 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.print(ERR_START_DATETIME_ALREADY_PASSED.get(sDate)); app.println(); app.println(); startDate = null; } } catch (ParseException pe) { app.println(ERR_START_DATETIME_FORMAT.get()); app.println(); } } uData.setStartDate(startDate); } private void askForTaskSchedule() throws ClientException { checkHeaderDisplay(); String schedule = null; while (schedule == null) { schedule = app.readInput(INFO_TASK_RECURRING_SCHEDULE_PROMPT.get(), null); try { RecurringTask.parseTaskTab(schedule); app.println(); } 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.println(); app.print(INFO_TASK_SCHEDULE_PROMPT_HEADER.get()); app.println(); headerDisplayed = true; } app.println(); } }