001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2010 Sun Microsystems, Inc.
015 * Portions Copyright 2014-2016 ForgeRock AS.
016 */
017package org.opends.server.admin.client.cli;
018
019import static org.opends.messages.ToolMessages.*;
020import static com.forgerock.opendj.cli.ArgumentConstants.*;
021
022import java.text.ParseException;
023import java.util.Collections;
024import java.util.Date;
025import java.util.EnumSet;
026import java.util.List;
027import java.util.Set;
028
029import org.forgerock.util.Utils;
030import org.opends.server.backends.task.FailedDependencyAction;
031import org.opends.server.backends.task.RecurringTask;
032import org.opends.server.types.DirectoryException;
033import org.opends.server.util.StaticUtils;
034
035import com.forgerock.opendj.cli.Argument;
036import com.forgerock.opendj.cli.ArgumentException;
037import com.forgerock.opendj.cli.ReturnCode;
038import com.forgerock.opendj.cli.StringArgument;
039import com.forgerock.opendj.cli.ClientException;
040
041/**
042 * A class that contains all the arguments related to the task scheduling.
043 *
044 */
045public class TaskScheduleArgs
046{
047  /**
048   * Magic value used to indicate that the user would like to schedule
049   * this operation to run immediately as a task as opposed to running
050   * the operation in the local VM.
051   */
052  public static final String NOW = "0";
053  /**
054   *  Argument for describing the task's start time.
055   */
056  public StringArgument startArg;
057
058  /**
059   *  Argument to indicate a recurring task.
060   */
061  public StringArgument recurringArg;
062
063  /**
064   *  Argument for specifying completion notifications.
065   */
066  public StringArgument completionNotificationArg;
067
068  /**
069   *  Argument for specifying error notifications.
070   */
071  public StringArgument errorNotificationArg;
072
073  /**
074   *  Argument for specifying dependency.
075   */
076  public StringArgument dependencyArg;
077
078  /**
079   *  Argument for specifying a failed dependency action.
080   */
081  public StringArgument failedDependencyActionArg;
082
083  /**
084   * Default constructor.
085   */
086  public TaskScheduleArgs()
087  {
088    try
089    {
090     createTaskArguments();
091    }
092    catch (ArgumentException ae)
093    {
094      // This is a bug.
095      throw new RuntimeException("Unexpected error: "+ae, ae);
096    }
097  }
098
099  private void createTaskArguments() throws ArgumentException
100  {
101    startArg =
102            StringArgument.builder(OPTION_LONG_START_DATETIME)
103                    .shortIdentifier(OPTION_SHORT_START_DATETIME)
104                    .description(INFO_DESCRIPTION_START_DATETIME.get())
105                    .valuePlaceholder(INFO_START_DATETIME_PLACEHOLDER.get())
106                    .buildArgument();
107    recurringArg =
108            StringArgument.builder(OPTION_LONG_RECURRING_TASK)
109                    .shortIdentifier(OPTION_SHORT_RECURRING_TASK)
110                    .description(INFO_DESCRIPTION_RECURRING_TASK.get())
111                    .valuePlaceholder(INFO_RECURRING_TASK_PLACEHOLDER.get())
112                    .buildArgument();
113    completionNotificationArg =
114            StringArgument.builder(OPTION_LONG_COMPLETION_NOTIFICATION_EMAIL)
115                    .shortIdentifier(OPTION_SHORT_COMPLETION_NOTIFICATION_EMAIL)
116                    .description(INFO_DESCRIPTION_TASK_COMPLETION_NOTIFICATION.get())
117                    .multiValued()
118                    .valuePlaceholder(INFO_EMAIL_ADDRESS_PLACEHOLDER.get())
119                    .buildArgument();
120    errorNotificationArg =
121            StringArgument.builder(OPTION_LONG_ERROR_NOTIFICATION_EMAIL)
122                    .shortIdentifier(OPTION_SHORT_ERROR_NOTIFICATION_EMAIL)
123                    .description(INFO_DESCRIPTION_TASK_ERROR_NOTIFICATION.get())
124                    .multiValued()
125                    .valuePlaceholder(INFO_EMAIL_ADDRESS_PLACEHOLDER.get())
126                    .buildArgument();
127    dependencyArg =
128            StringArgument.builder(OPTION_LONG_DEPENDENCY)
129                    .shortIdentifier(OPTION_SHORT_DEPENDENCY)
130                    .description(INFO_DESCRIPTION_TASK_DEPENDENCY_ID.get())
131                    .multiValued()
132                    .valuePlaceholder(INFO_TASK_ID_PLACEHOLDER.get())
133                    .buildArgument();
134
135    final Set<FailedDependencyAction> fdaValSet = EnumSet.allOf(FailedDependencyAction.class);
136    failedDependencyActionArg =
137            StringArgument.builder(OPTION_LONG_FAILED_DEPENDENCY_ACTION)
138            .shortIdentifier(OPTION_SHORT_FAILED_DEPENDENCY_ACTION)
139            .description(INFO_DESCRIPTION_TASK_FAILED_DEPENDENCY_ACTION.get(
140                    Utils.joinAsString(",", fdaValSet), FailedDependencyAction.defaultValue().name()))
141            .multiValued()
142            .valuePlaceholder(INFO_ACTION_PLACEHOLDER.get())
143            .buildArgument();
144  }
145
146  /**
147   * Returns all the task schedule related arguments.
148   * @return all the task schedule related arguments.
149   */
150  public Argument[] getArguments()
151  {
152    return new Argument[] {startArg, recurringArg, completionNotificationArg,
153       errorNotificationArg, dependencyArg, failedDependencyActionArg};
154  }
155
156  /**
157   * Validates arguments related to task scheduling.  This should be
158   * called after the <code>ArgumentParser.parseArguments</code> has
159   * been called.
160   * <br>
161   * Note that this method does only validation that is not dependent on whether
162   * the operation will be launched as a task or not.  If the operation is not
163   * to be launched as a task, the method {@link #validateArgsIfOffline()}
164   * should be called instead of this method.
165   * @throws ArgumentException if there is a problem with the arguments.
166   * @throws ClientException if there is a problem with one of the values provided
167   * by the user.
168   */
169  public void validateArgs() throws ArgumentException, ClientException
170  {
171    if (startArg.isPresent() && !NOW.equals(startArg.getValue())) {
172      try {
173        Date date = StaticUtils.parseDateTimeString(startArg.getValue());
174        // Check that the provided date is not previous to the current date.
175        Date currentDate = new Date(System.currentTimeMillis());
176        if (currentDate.after(date))
177        {
178          throw new ClientException(ReturnCode.ERROR_USER_DATA, ERR_START_DATETIME_ALREADY_PASSED.get(
179              startArg.getValue()));
180        }
181      } catch (ParseException pe) {
182        throw new ArgumentException(ERR_START_DATETIME_FORMAT.get());
183      }
184    }
185
186    if (recurringArg.isPresent())
187    {
188      try
189      {
190        RecurringTask.parseTaskTab(recurringArg.getValue());
191      }
192      catch (DirectoryException de)
193      {
194        throw new ArgumentException(ERR_RECURRING_SCHEDULE_FORMAT_ERROR.get(
195            de.getMessageObject()), de);
196      }
197    }
198
199    checkEmailArgument(completionNotificationArg);
200    checkEmailArgument(errorNotificationArg);
201
202    if (failedDependencyActionArg.isPresent()) {
203
204      if (!dependencyArg.isPresent()) {
205        throw new ArgumentException(ERR_TASKTOOL_FDA_WITH_NO_DEPENDENCY.get());
206      }
207
208      String fda = failedDependencyActionArg.getValue();
209      if (null == FailedDependencyAction.fromString(fda)) {
210        Set<FailedDependencyAction> fdaValSet =
211          EnumSet.allOf(FailedDependencyAction.class);
212        throw new ArgumentException(ERR_TASKTOOL_INVALID_FDA.get(fda,
213            Utils.joinAsString(",", fdaValSet)));
214      }
215    }
216  }
217
218  private void checkEmailArgument(final StringArgument argument) throws ArgumentException {
219    if (argument.isPresent()) {
220      for (final String email : argument.getValues()) {
221        if (!StaticUtils.isEmailAddress(email)) {
222          throw new ArgumentException(ERR_TASKTOOL_INVALID_EMAIL_ADDRESS.get(email, argument.getLongIdentifier()));
223        }
224      }
225    }
226  }
227
228  /**
229   * Validates arguments related to task scheduling.  This should be
230   * called after the <code>ArgumentParser.parseArguments</code> has
231   * been called.
232   * <br>
233   * This method assumes that the operation is not to be launched as a task.
234   * This method covers all the checks done by {@link #validateArgs()}, so it
235   * is not necessary to call that method if this method is being called.
236   * @throws ArgumentException if there is a problem with the arguments.
237   * @throws ClientException if there is a problem with one of the values provided
238   * by the user.
239   */
240  public void validateArgsIfOffline() throws ArgumentException, ClientException
241  {
242    Argument[] incompatibleArgs = {startArg, recurringArg,
243        completionNotificationArg,
244        errorNotificationArg, dependencyArg, failedDependencyActionArg};
245    for (Argument arg : incompatibleArgs)
246    {
247      if (arg.isPresent()) {
248        throw new ArgumentException(ERR_TASKTOOL_OPTIONS_FOR_TASK_ONLY.get(
249                arg.getLongIdentifier()));
250      }
251    }
252    validateArgs();
253  }
254
255  /**
256   * Gets the date at which the associated task should be scheduled to start.
257   *
258   * @return date/time at which the task should be scheduled
259   */
260  public Date getStartDateTime() {
261    Date start = null;
262
263    // If the start time arg is present parse its value
264    if (startArg != null && startArg.isPresent()) {
265      if (NOW.equals(startArg.getValue())) {
266        start = new Date();
267      } else {
268        try {
269          start = StaticUtils.parseDateTimeString(startArg.getValue());
270        } catch (ParseException pe) {
271          // ignore; validated in validateTaskArgs()
272        }
273      }
274    }
275    return start;
276  }
277
278  /**
279   * Whether the arguments provided by the user, indicate that the task should
280   * be executed immediately.
281   * <br>
282   * This method assumes that the arguments have already been parsed and
283   * validated.
284   * @return {@code true} if the task must be executed immediately and
285   * {@code false} otherwise.
286   */
287  public boolean isStartNow()
288  {
289    boolean isStartNow = true;
290    if (startArg != null && startArg.isPresent()) {
291      isStartNow = NOW.equals(startArg.getValue());
292    }
293    return isStartNow;
294  }
295
296  /**
297   * Gets the date/time pattern for recurring task schedule.
298   *
299   * @return recurring date/time pattern at which the task
300   *         should be scheduled.
301   */
302  public String getRecurringDateTime() {
303    String pattern = null;
304
305    // If the recurring task arg is present parse its value
306    if (recurringArg != null && recurringArg.isPresent()) {
307      pattern = recurringArg.getValue();
308    }
309    return pattern;
310  }
311
312  /**
313   * Gets a list of task IDs upon which the associated task is dependent.
314   *
315   * @return list of task IDs
316   */
317  public List<String> getDependencyIds() {
318    if (dependencyArg.isPresent()) {
319      return dependencyArg.getValues();
320    } else {
321      return Collections.emptyList();
322    }
323  }
324
325  /**
326   * Gets the action to take should one of the dependent task fail.
327   *
328   * @return action to take
329   */
330  public FailedDependencyAction getFailedDependencyAction() {
331    FailedDependencyAction fda = null;
332    if (failedDependencyActionArg.isPresent()) {
333      String fdaString = failedDependencyActionArg.getValue();
334      fda = FailedDependencyAction.fromString(fdaString);
335    }
336    return fda;
337  }
338
339  /**
340   * Gets a list of email address to which an email will be sent when this
341   * task completes.
342   *
343   * @return list of email addresses
344   */
345  public List<String> getNotifyUponCompletionEmailAddresses() {
346    if (completionNotificationArg.isPresent()) {
347      return completionNotificationArg.getValues();
348    } else {
349      return Collections.emptyList();
350    }
351  }
352
353  /**
354   * Gets a list of email address to which an email will be sent if this
355   * task encounters an error during execution.
356   *
357   * @return list of email addresses
358   */
359  public List<String> getNotifyUponErrorEmailAddresses() {
360    if (errorNotificationArg.isPresent()) {
361      return errorNotificationArg.getValues();
362    } else {
363      return Collections.emptyList();
364    }
365  }
366}