001/*
002 * Copyright 2018-2019 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2018-2019 Ping Identity Corporation
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021package com.unboundid.ldap.sdk.unboundidds.tasks;
022
023
024
025import java.util.Arrays;
026import java.util.Collections;
027import java.util.Date;
028import java.util.LinkedHashMap;
029import java.util.LinkedList;
030import java.util.List;
031import java.util.Map;
032
033import com.unboundid.ldap.sdk.Attribute;
034import com.unboundid.ldap.sdk.Entry;
035import com.unboundid.util.NotMutable;
036import com.unboundid.util.StaticUtils;
037import com.unboundid.util.ThreadSafety;
038import com.unboundid.util.ThreadSafetyLevel;
039
040import static com.unboundid.ldap.sdk.unboundidds.tasks.TaskMessages.*;
041
042
043
044/**
045 * This class defines a Directory Server task that can be used to cause the
046 * server to execute a specified command with a given set of arguments.
047 * <BR>
048 * <BLOCKQUOTE>
049 *   <B>NOTE:</B>  This class, and other classes within the
050 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
051 *   supported for use against Ping Identity, UnboundID, and
052 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
053 *   for proprietary functionality or for external specifications that are not
054 *   considered stable or mature enough to be guaranteed to work in an
055 *   interoperable way with other types of LDAP servers.
056 * </BLOCKQUOTE>
057 * <BR>
058 * The server imposes limitation on the commands that can be executed and on the
059 * circumstances in which they can be invoked.  See the
060 * exec-command-whitelist.txt file in the server's config directory for a
061 * summary of these restrictions, and for additional information about exec
062 * tasks.
063 * <BR><BR>
064 * The properties that are available for use with this type of task include:
065 * <UL>
066 *   <LI>The absolute path to the command to execute.  This must be
067 *       provided.</LI>
068 *   <LI>An optional string with arguments to provide to the command.</LI>
069 *   <LI>An optional path to a file to which the command's output should be
070 *       written.</LI>
071 *   <LI>An optional boolean flag that indicates whether to log the command's
072 *       output to the server error log.</LI>
073 *   <LI>An optional string that specifies the task state that should be used
074 *       if the command completes with a nonzero exit code.</LI>
075 * </UL>
076 */
077@NotMutable()
078@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
079public final class ExecTask
080       extends Task
081{
082  /**
083   * The fully-qualified name of the Java class that is used for the exec task.
084   */
085  static final String EXEC_TASK_CLASS =
086       "com.unboundid.directory.server.tasks.ExecTask";
087
088
089
090  /**
091   * The name of the attribute used to specify the absolute path for the command
092   * to be executed.
093   */
094  private static final String ATTR_COMMAND_PATH = "ds-task-exec-command-path";
095
096
097
098  /**
099   * The name of the attribute used to specify the argument string to provide
100   * when running the command.
101   */
102  private static final String ATTR_COMMAND_ARGUMENTS =
103       "ds-task-exec-command-arguments";
104
105
106
107  /**
108   * The name of the attribute used to specify the path to a file in which the
109   * command's output should be recorded.
110   */
111  private static final String ATTR_COMMAND_OUTPUT_FILE =
112       "ds-task-exec-command-output-file";
113
114
115
116  /**
117   * The name of the attribute used to indicate whether to record the command's
118   * output in the server error log.
119   */
120  private static final String ATTR_LOG_COMMAND_OUTPUT =
121       "ds-task-exec-log-command-output";
122
123
124
125  /**
126   * The name of the attribute used to specify the task state for commands that
127   * complete with a nonzero exit code.
128   */
129  private static final String ATTR_TASK_STATE_FOR_NONZERO_EXIT_CODE =
130       "ds-task-exec-task-completion-state-for-nonzero-exit-code";
131
132
133
134  /**
135   * The name of the object class used in EXEC task entries.
136   */
137  private static final String OC_EXEC_TASK = "ds-task-exec";
138
139
140
141  /**
142   * The task property that will be used for the command path.
143   */
144  private static final TaskProperty PROPERTY_COMMAND_PATH =
145     new TaskProperty(ATTR_COMMAND_PATH,
146          INFO_EXEC_DISPLAY_NAME_COMMAND_PATH.get(),
147          INFO_EXEC_DESCRIPTION_COMMAND_PATH.get(), String.class, true, false,
148          false);
149
150
151
152  /**
153   * The task property that will be used for the command arguments.
154   */
155  private static final TaskProperty PROPERTY_COMMAND_ARGUMENTS =
156     new TaskProperty(ATTR_COMMAND_ARGUMENTS,
157          INFO_EXEC_DISPLAY_NAME_COMMAND_ARGUMENTS.get(),
158          INFO_EXEC_DESCRIPTION_COMMAND_ARGUMENTS.get(), String.class, false,
159          false, false);
160
161
162
163  /**
164   * The task property that will be used for the command output file.
165   */
166  private static final TaskProperty PROPERTY_COMMAND_OUTPUT_FILE =
167     new TaskProperty(ATTR_COMMAND_OUTPUT_FILE,
168          INFO_EXEC_DISPLAY_NAME_COMMAND_OUTPUT_FILE.get(),
169          INFO_EXEC_DESCRIPTION_COMMAND_OUTPUT_FILE.get(), String.class, false,
170          false, false);
171
172
173
174  /**
175   * The task property that will be used for the log command output flag.
176   */
177  private static final TaskProperty PROPERTY_LOG_COMMAND_OUTPUT =
178     new TaskProperty(ATTR_LOG_COMMAND_OUTPUT,
179          INFO_EXEC_DISPLAY_NAME_LOG_COMMAND_OUTPUT.get(),
180          INFO_EXEC_DESCRIPTION_LOG_COMMAND_OUTPUT.get(), Boolean.class, false,
181          false, false);
182
183
184
185  /**
186   * The task property that will be used for the task state for commands that
187   * complete with a nonzero exit code.
188   */
189  private static final TaskProperty PROPERTY_TASK_STATE_FOR_NONZERO_EXIT_CODE =
190     new TaskProperty(ATTR_TASK_STATE_FOR_NONZERO_EXIT_CODE,
191          INFO_EXEC_DISPLAY_NAME_TASK_STATE_FOR_NONZERO_EXIT_CODE.get(),
192          INFO_EXEC_DESCRIPTION_NAME_TASK_STATE_FOR_NONZERO_EXIT_CODE.get(),
193          String.class, false, false, false,
194          new String[]
195          {
196            "STOPPED_BY_ERROR",
197            "STOPPED-BY-ERROR",
198            "COMPLETED_WITH_ERRORS",
199            "COMPLETED-WITH-ERRORS",
200            "COMPLETED_SUCCESSFULLY",
201            "COMPLETED-SUCCESSFULLY"
202          });
203
204
205
206  /**
207   * The serial version UID for this serializable class.
208   */
209  private static final long serialVersionUID = -6541429978844959603L;
210
211
212
213  // Indicates whether command output is to be logged.
214  private final Boolean logCommandOutput;
215
216  // The arguments to provide when executing the command.
217  private final String commandArguments;
218
219  // The path to the file to which command output should be written.
220  private final String commandOutputFile;
221
222  // The path to the command to be executed.
223  private final String commandPath;
224
225  // The name of the task state that should be used if the command completes
226  // with a nonzero exit code.
227  private final String taskStateForNonZeroExitCode;
228
229
230
231  /**
232   * Creates a new, uninitialized exec task instance that should only be used
233   * for obtaining general information about this task, including the task name,
234   * description, and supported properties.  Attempts to use a task created with
235   * this constructor for any other reason will likely fail.
236   */
237  public ExecTask()
238  {
239    commandPath = null;
240    commandArguments = null;
241    commandOutputFile = null;
242    logCommandOutput = null;
243    taskStateForNonZeroExitCode = null;
244  }
245
246
247
248  /**
249   * Creates a new exec task with the provided information.
250   *
251   * @param  commandPath
252   *              The absolute path (on the server filesystem) to the command
253   *              that should be executed.  This must not be {@code null}.
254   * @param  commandArguments
255   *              The complete set of arguments that should be used when
256   *              running the command.  This may be {@code null} if no arguments
257   *              should be provided.
258   * @param  commandOutputFile
259   *              The path to an output file that should be used to record all
260   *              output that the command writes to standard output or standard
261   *              error.  This may be {@code null} if the command output should
262   *              not be recorded in a file.
263   * @param  logCommandOutput
264   *              Indicates whether to record the command output in the server
265   *              error log.  If this is {@code true}, then all non-blank lines
266   *              that the command writes to standard output or standard error
267   *              will be recorded in the server error log.  if this is
268   *              {@code false}, then the output will not be recorded in the
269   *              server error log.  If this is {@code null}, then the server
270   *              will determine whether to log command output.  Note that a
271   *              value of {@code true} should only be used if you are certain
272   *              that the tool will only generate text-based output, and you
273   *              should use {@code false} if you know that the command may
274   *              generate non-text output.
275   * @param  taskStateForNonZeroExitCode
276   *              The task state that should be used if the command completes
277   *              with a nonzero exit code.  This may be {@code null} to
278   *              indicate that the server should determine the appropriate task
279   *              state.  If it is non-{@code null}, then the value must be one
280   *              of {@link TaskState#STOPPED_BY_ERROR},
281   *              {@link TaskState#COMPLETED_WITH_ERRORS}, or
282   *              {@link TaskState#COMPLETED_SUCCESSFULLY}.
283   *
284   * @throws  TaskException  If there is a problem with any of the provided
285   *                         arguments.
286   */
287  public ExecTask(final String commandPath, final String commandArguments,
288                  final String commandOutputFile,
289                  final Boolean logCommandOutput,
290                  final TaskState taskStateForNonZeroExitCode)
291         throws TaskException
292  {
293    this(null, commandPath, commandArguments, commandOutputFile,
294         logCommandOutput, taskStateForNonZeroExitCode, null, null, null, null,
295         null);
296  }
297
298
299
300  /**
301   * Creates a new exec task with the provided information.
302   *
303   * @param  taskID
304   *              The task ID to use for this task.  If it is {@code null} then
305   *              a UUID will be generated for use as the task ID.
306   * @param  commandPath
307   *              The absolute path (on the server filesystem) to the command
308   *              that should be executed.  This must not be {@code null}.
309   * @param  commandArguments
310   *              The complete set of arguments that should be used when
311   *              running the command.  This may be {@code null} if no arguments
312   *              should be provided.
313   * @param  commandOutputFile
314   *              The path to an output file that should be used to record all
315   *              output that the command writes to standard output or standard
316   *              error.  This may be {@code null} if the command output should
317   *              not be recorded in a file.
318   * @param  logCommandOutput
319   *              Indicates whether to record the command output in the server
320   *              error log.  If this is {@code true}, then all non-blank lines
321   *              that the command writes to standard output or standard error
322   *              will be recorded in the server error log.  if this is
323   *              {@code false}, then the output will not be recorded in the
324   *              server error log.  If this is {@code null}, then the server
325   *              will determine whether to log command output.  Note that a
326   *              value of {@code true} should only be used if you are certain
327   *              that the tool will only generate text-based output, and you
328   *              should use {@code false} if you know that the command may
329   *              generate non-text output.
330   * @param  taskStateForNonZeroExitCode
331   *              The task state that should be used if the command completes
332   *              with a nonzero exit code.  This may be {@code null} to
333   *              indicate that the server should determine the appropriate task
334   *              state.  If it is non-{@code null}, then the value must be one
335   *              of {@link TaskState#STOPPED_BY_ERROR},
336   *              {@link TaskState#COMPLETED_WITH_ERRORS}, or
337   *              {@link TaskState#COMPLETED_SUCCESSFULLY}.
338   * @param  scheduledStartTime
339   *              The time that this task should start running.
340   * @param  dependencyIDs
341   *              The list of task IDs that will be required to complete before
342   *              this task will be eligible to start.
343   * @param  failedDependencyAction
344   *              Indicates what action should be taken if any of the
345   *              dependencies for this task do not complete successfully.
346   * @param  notifyOnCompletion
347   *              The list of e-mail addresses of individuals that should be
348   *              notified when this task completes.
349   * @param  notifyOnError
350   *              The list of e-mail addresses of individuals that should be
351   *              notified if this task does not complete successfully.
352   *
353   * @throws  TaskException  If there is a problem with any of the provided
354   *                         arguments.
355   */
356  public ExecTask(final String taskID, final String commandPath,
357                  final String commandArguments, final String commandOutputFile,
358                  final Boolean logCommandOutput,
359                  final TaskState taskStateForNonZeroExitCode,
360                  final Date scheduledStartTime,
361                  final List<String> dependencyIDs,
362                  final FailedDependencyAction failedDependencyAction,
363                  final List<String> notifyOnCompletion,
364                  final List<String> notifyOnError)
365         throws TaskException
366  {
367    this(taskID, commandPath, commandArguments, commandOutputFile,
368         logCommandOutput, taskStateForNonZeroExitCode, scheduledStartTime,
369         dependencyIDs, failedDependencyAction, null, notifyOnCompletion,
370         null, notifyOnError, null, null, null);
371  }
372
373
374
375  /**
376   * Creates a new exec task with the provided information.
377   *
378   * @param  taskID
379   *              The task ID to use for this task.  If it is {@code null} then
380   *              a UUID will be generated for use as the task ID.
381   * @param  commandPath
382   *              The absolute path (on the server filesystem) to the command
383   *              that should be executed.  This must not be {@code null}.
384   * @param  commandArguments
385   *              The complete set of arguments that should be used when
386   *              running the command.  This may be {@code null} if no arguments
387   *              should be provided.
388   * @param  commandOutputFile
389   *              The path to an output file that should be used to record all
390   *              output that the command writes to standard output or standard
391   *              error.  This may be {@code null} if the command output should
392   *              not be recorded in a file.
393   * @param  logCommandOutput
394   *              Indicates whether to record the command output in the server
395   *              error log.  If this is {@code true}, then all non-blank lines
396   *              that the command writes to standard output or standard error
397   *              will be recorded in the server error log.  if this is
398   *              {@code false}, then the output will not be recorded in the
399   *              server error log.  If this is {@code null}, then the server
400   *              will determine whether to log command output.  Note that a
401   *              value of {@code true} should only be used if you are certain
402   *              that the tool will only generate text-based output, and you
403   *              should use {@code false} if you know that the command may
404   *              generate non-text output.
405   * @param  taskStateForNonZeroExitCode
406   *              The task state that should be used if the command completes
407   *              with a nonzero exit code.  This may be {@code null} to
408   *              indicate that the server should determine the appropriate task
409   *              state.  If it is non-{@code null}, then the value must be one
410   *              of {@link TaskState#STOPPED_BY_ERROR},
411   *              {@link TaskState#COMPLETED_WITH_ERRORS}, or
412   *              {@link TaskState#COMPLETED_SUCCESSFULLY}.
413   * @param  scheduledStartTime
414   *              The time that this task should start running.
415   * @param  dependencyIDs
416   *              The list of task IDs that will be required to complete before
417   *              this task will be eligible to start.
418   * @param  failedDependencyAction
419   *              Indicates what action should be taken if any of the
420   *              dependencies for this task do not complete successfully.
421   * @param  notifyOnStart
422   *              The list of e-mail addresses of individuals that should be
423   *              notified when this task starts.
424   * @param  notifyOnCompletion
425   *              The list of e-mail addresses of individuals that should be
426   *              notified when this task completes.
427   * @param  notifyOnSuccess
428   *              The list of e-mail addresses of individuals that should be
429   *              notified if this task completes successfully.
430   * @param  notifyOnError
431   *              The list of e-mail addresses of individuals that should be
432   *              notified if this task does not complete successfully.
433   * @param  alertOnStart
434   *              Indicates whether the server should send an alert notification
435   *              when this task starts.
436   * @param  alertOnSuccess
437   *              Indicates whether the server should send an alert notification
438   *              if this task completes successfully.
439   * @param  alertOnError
440   *              Indicates whether the server should send an alert notification
441   *              if this task fails to complete successfully.
442   *
443   * @throws  TaskException  If there is a problem with any of the provided
444   *                         arguments.
445   */
446  public ExecTask(final String taskID, final String commandPath,
447                  final String commandArguments, final String commandOutputFile,
448                  final Boolean logCommandOutput,
449                  final TaskState taskStateForNonZeroExitCode,
450                  final Date scheduledStartTime,
451                  final List<String> dependencyIDs,
452                  final FailedDependencyAction failedDependencyAction,
453                  final List<String> notifyOnStart,
454                  final List<String> notifyOnCompletion,
455                  final List<String> notifyOnSuccess,
456                  final List<String> notifyOnError, final Boolean alertOnStart,
457                  final Boolean alertOnSuccess, final Boolean alertOnError)
458         throws TaskException
459  {
460    super(taskID, EXEC_TASK_CLASS, scheduledStartTime, dependencyIDs,
461         failedDependencyAction, notifyOnStart, notifyOnCompletion,
462         notifyOnSuccess, notifyOnError, alertOnStart, alertOnSuccess,
463         alertOnError);
464
465    this.commandPath = commandPath;
466    this.commandArguments = commandArguments;
467    this.commandOutputFile = commandOutputFile;
468    this.logCommandOutput = logCommandOutput;
469
470    if ((commandPath == null) || commandPath.isEmpty())
471    {
472      throw new TaskException(ERR_EXEC_MISSING_PATH.get());
473    }
474
475    if (taskStateForNonZeroExitCode == null)
476    {
477      this.taskStateForNonZeroExitCode = null;
478    }
479    else
480    {
481      switch (taskStateForNonZeroExitCode)
482      {
483        case STOPPED_BY_ERROR:
484        case COMPLETED_WITH_ERRORS:
485        case COMPLETED_SUCCESSFULLY:
486          this.taskStateForNonZeroExitCode = taskStateForNonZeroExitCode.name();
487          break;
488        default:
489          throw new TaskException(
490               ERR_EXEC_INVALID_STATE_FOR_NONZERO_EXIT_CODE.get(
491                    TaskState.STOPPED_BY_ERROR.name(),
492                    TaskState.COMPLETED_WITH_ERRORS.name(),
493                    TaskState.COMPLETED_SUCCESSFULLY.name()));
494      }
495    }
496  }
497
498
499
500  /**
501   * Creates a new exec task from the provided entry.
502   *
503   * @param  entry  The entry to use to create this exec task.
504   *
505   * @throws  TaskException  If the provided entry cannot be parsed as an exec
506   *                         task entry.
507   */
508  public ExecTask(final Entry entry)
509         throws TaskException
510  {
511    super(entry);
512
513
514    // Get the command to execute.  It must be provided.
515    commandPath = entry.getAttributeValue(ATTR_COMMAND_PATH);
516    if (commandPath == null)
517    {
518      throw new TaskException(ERR_EXEC_ENTRY_MISSING_COMMAND_PATH.get(
519           entry.getDN(), ATTR_COMMAND_PATH));
520    }
521
522    commandArguments = entry.getAttributeValue(ATTR_COMMAND_ARGUMENTS);
523    commandOutputFile = entry.getAttributeValue(ATTR_COMMAND_OUTPUT_FILE);
524    logCommandOutput =
525         entry.getAttributeValueAsBoolean(ATTR_LOG_COMMAND_OUTPUT);
526    taskStateForNonZeroExitCode =
527         entry.getAttributeValue(ATTR_TASK_STATE_FOR_NONZERO_EXIT_CODE);
528  }
529
530
531
532  /**
533   * Creates a new exec task from the provided set of task properties.
534   *
535   * @param  properties  The set of task properties and their corresponding
536   *                     values to use for the task.  It must not be
537   *                     {@code null}.
538   *
539   * @throws  TaskException  If the provided set of properties cannot be used to
540   *                         create a valid exec task.
541   */
542  public ExecTask(final Map<TaskProperty,List<Object>> properties)
543         throws TaskException
544  {
545    super(EXEC_TASK_CLASS, properties);
546
547    String path = null;
548    String arguments = null;
549    String outputFile = null;
550    Boolean logOutput = null;
551    String nonZeroExitState = null;
552    for (final Map.Entry<TaskProperty,List<Object>> entry :
553         properties.entrySet())
554    {
555      final TaskProperty p = entry.getKey();
556      final String attrName = StaticUtils.toLowerCase(p.getAttributeName());
557      final List<Object> values = entry.getValue();
558
559      if (attrName.equals(ATTR_COMMAND_PATH))
560      {
561        path = parseString(p, values, path);
562      }
563      else if (attrName.equals(ATTR_COMMAND_ARGUMENTS))
564      {
565        arguments = parseString(p, values, arguments);
566      }
567      else if (attrName.equals(ATTR_COMMAND_OUTPUT_FILE))
568      {
569        outputFile = parseString(p, values, outputFile);
570      }
571      else if (attrName.equals(ATTR_LOG_COMMAND_OUTPUT))
572      {
573        logOutput = parseBoolean(p, values, logOutput);
574      }
575      else if (attrName.equals(ATTR_TASK_STATE_FOR_NONZERO_EXIT_CODE))
576      {
577        nonZeroExitState = parseString(p, values, nonZeroExitState);
578      }
579    }
580
581    commandPath = path;
582    commandArguments = arguments;
583    commandOutputFile = outputFile;
584    logCommandOutput = logOutput;
585    taskStateForNonZeroExitCode = nonZeroExitState;
586
587    if (commandPath == null)
588    {
589      throw new TaskException(ERR_EXEC_PROPERTIES_MISSING_COMMAND_PATH.get());
590    }
591  }
592
593
594
595  /**
596   * {@inheritDoc}
597   */
598  @Override()
599  public String getTaskName()
600  {
601    return INFO_TASK_NAME_EXEC.get();
602  }
603
604
605
606  /**
607   * {@inheritDoc}
608   */
609  @Override()
610  public String getTaskDescription()
611  {
612    return INFO_TASK_DESCRIPTION_EXEC.get();
613  }
614
615
616
617  /**
618   * Retrieves the path to the command to be executed.
619   *
620   * @return  The path to the command to be executed.
621   */
622  public String getCommandPath()
623  {
624    return commandPath;
625  }
626
627
628
629  /**
630   * Retrieves a string with the values of the arguments that should be provided
631   * when running the command.
632   *
633   * @return  A string with the values of the arguments that should be provided
634   *          when running the command, or {@code null} if the command should be
635   *          run without any arguments.
636   */
637  public String getCommandArguments()
638  {
639    return commandArguments;
640  }
641
642
643
644  /**
645   * Retrieves the path to a file to which the command's output should be
646   * written.
647   *
648   * @return  The path to a file to which the command's output should be
649   *          written, or {@code null} if the output should not be written to a
650   *          file.
651   */
652  public String getCommandOutputFile()
653  {
654    return commandOutputFile;
655  }
656
657
658
659  /**
660   * Indicates whether the command's output should be recorded in the server's
661   * error log.
662   *
663   * @return  {@code true} if the command's output should be recorded in the
664   *          server's error log, {@code false} if the output should not be
665   *          logged, or {@code null} if the task should not specify the
666   *          behavior.
667   */
668  public Boolean logCommandOutput()
669  {
670    return logCommandOutput;
671  }
672
673
674
675  /**
676   * Retrieves a string representation of the task state that should be returned
677   * if the command completes with a nonzero exit code.
678   *
679   * @return  A string representation of the task state that should be returned
680   *          if the command completes with a nonzero exit state, or
681   *          {@code null} if the task should not specify the return state.
682   */
683  public String getTaskStateForNonZeroExitCode()
684  {
685    return taskStateForNonZeroExitCode;
686  }
687
688
689
690  /**
691   * {@inheritDoc}
692   */
693  @Override()
694  protected List<String> getAdditionalObjectClasses()
695  {
696    return Collections.singletonList(OC_EXEC_TASK);
697  }
698
699
700
701  /**
702   * {@inheritDoc}
703   */
704  @Override()
705  protected List<Attribute> getAdditionalAttributes()
706  {
707    final LinkedList<Attribute> attrList = new LinkedList<>();
708    attrList.add(new Attribute(ATTR_COMMAND_PATH, commandPath));
709
710    if (commandArguments != null)
711    {
712      attrList.add(new Attribute(ATTR_COMMAND_ARGUMENTS, commandArguments));
713    }
714
715    if (commandOutputFile != null)
716    {
717      attrList.add(new Attribute(ATTR_COMMAND_OUTPUT_FILE, commandOutputFile));
718    }
719
720    if (logCommandOutput != null)
721    {
722      attrList.add(new Attribute(ATTR_LOG_COMMAND_OUTPUT,
723           String.valueOf(logCommandOutput)));
724    }
725
726    if (taskStateForNonZeroExitCode != null)
727    {
728      attrList.add(new Attribute(ATTR_TASK_STATE_FOR_NONZERO_EXIT_CODE,
729           taskStateForNonZeroExitCode));
730    }
731
732    return attrList;
733  }
734
735
736
737  /**
738   * {@inheritDoc}
739   */
740  @Override()
741  public List<TaskProperty> getTaskSpecificProperties()
742  {
743    return Collections.unmodifiableList(Arrays.asList(
744         PROPERTY_COMMAND_PATH, PROPERTY_COMMAND_ARGUMENTS,
745         PROPERTY_COMMAND_OUTPUT_FILE, PROPERTY_LOG_COMMAND_OUTPUT,
746         PROPERTY_TASK_STATE_FOR_NONZERO_EXIT_CODE));
747  }
748
749
750
751  /**
752   * {@inheritDoc}
753   */
754  @Override()
755  public Map<TaskProperty,List<Object>> getTaskPropertyValues()
756  {
757    final LinkedHashMap<TaskProperty, List<Object>> props =
758         new LinkedHashMap<>(StaticUtils.computeMapCapacity(5));
759
760    props.put(PROPERTY_COMMAND_PATH,
761         Collections.<Object>singletonList(commandPath));
762
763    if (commandArguments != null)
764    {
765      props.put(PROPERTY_COMMAND_ARGUMENTS,
766           Collections.<Object>singletonList(commandArguments));
767    }
768
769    if (commandOutputFile != null)
770    {
771      props.put(PROPERTY_COMMAND_OUTPUT_FILE,
772           Collections.<Object>singletonList(commandOutputFile));
773    }
774
775    if (logCommandOutput != null)
776    {
777      props.put(PROPERTY_LOG_COMMAND_OUTPUT,
778           Collections.<Object>singletonList(logCommandOutput));
779    }
780
781    if (taskStateForNonZeroExitCode != null)
782    {
783      props.put(PROPERTY_TASK_STATE_FOR_NONZERO_EXIT_CODE,
784           Collections.<Object>singletonList(taskStateForNonZeroExitCode));
785    }
786
787    return Collections.unmodifiableMap(props);
788  }
789}