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