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 * <LI>An optional string that specifies the path to the working directory to 076 * use when executing the command.</LI> 077 * </UL> 078 */ 079@NotMutable() 080@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 081public final class ExecTask 082 extends Task 083{ 084 /** 085 * The fully-qualified name of the Java class that is used for the exec task. 086 */ 087 static final String EXEC_TASK_CLASS = 088 "com.unboundid.directory.server.tasks.ExecTask"; 089 090 091 092 /** 093 * The name of the attribute used to specify the absolute path for the command 094 * to be executed. 095 */ 096 private static final String ATTR_COMMAND_PATH = "ds-task-exec-command-path"; 097 098 099 100 /** 101 * The name of the attribute used to specify the argument string to provide 102 * when running the command. 103 */ 104 private static final String ATTR_COMMAND_ARGUMENTS = 105 "ds-task-exec-command-arguments"; 106 107 108 109 /** 110 * The name of the attribute used to specify the path to a file in which the 111 * command's output should be recorded. 112 */ 113 private static final String ATTR_COMMAND_OUTPUT_FILE = 114 "ds-task-exec-command-output-file"; 115 116 117 118 /** 119 * The name of the attribute used to indicate whether to record the command's 120 * output in the server error log. 121 */ 122 private static final String ATTR_LOG_COMMAND_OUTPUT = 123 "ds-task-exec-log-command-output"; 124 125 126 127 /** 128 * The name of the attribute used to specify the task state for commands that 129 * complete with a nonzero exit code. 130 */ 131 private static final String ATTR_TASK_STATE_FOR_NONZERO_EXIT_CODE = 132 "ds-task-exec-task-completion-state-for-nonzero-exit-code"; 133 134 135 136 /** 137 * The name of the attribute used to specify the path to the working directory 138 * to use when executing the command. 139 */ 140 private static final String ATTR_WORKING_DIRECTORY = 141 "ds-task-exec-working-directory"; 142 143 144 145 /** 146 * The name of the object class used in EXEC task entries. 147 */ 148 private static final String OC_EXEC_TASK = "ds-task-exec"; 149 150 151 152 /** 153 * The task property that will be used for the command path. 154 */ 155 private static final TaskProperty PROPERTY_COMMAND_PATH = 156 new TaskProperty(ATTR_COMMAND_PATH, 157 INFO_EXEC_DISPLAY_NAME_COMMAND_PATH.get(), 158 INFO_EXEC_DESCRIPTION_COMMAND_PATH.get(), String.class, true, false, 159 false); 160 161 162 163 /** 164 * The task property that will be used for the command arguments. 165 */ 166 private static final TaskProperty PROPERTY_COMMAND_ARGUMENTS = 167 new TaskProperty(ATTR_COMMAND_ARGUMENTS, 168 INFO_EXEC_DISPLAY_NAME_COMMAND_ARGUMENTS.get(), 169 INFO_EXEC_DESCRIPTION_COMMAND_ARGUMENTS.get(), String.class, false, 170 false, false); 171 172 173 174 /** 175 * The task property that will be used for the command output file. 176 */ 177 private static final TaskProperty PROPERTY_COMMAND_OUTPUT_FILE = 178 new TaskProperty(ATTR_COMMAND_OUTPUT_FILE, 179 INFO_EXEC_DISPLAY_NAME_COMMAND_OUTPUT_FILE.get(), 180 INFO_EXEC_DESCRIPTION_COMMAND_OUTPUT_FILE.get(), String.class, false, 181 false, false); 182 183 184 185 /** 186 * The task property that will be used for the log command output flag. 187 */ 188 private static final TaskProperty PROPERTY_LOG_COMMAND_OUTPUT = 189 new TaskProperty(ATTR_LOG_COMMAND_OUTPUT, 190 INFO_EXEC_DISPLAY_NAME_LOG_COMMAND_OUTPUT.get(), 191 INFO_EXEC_DESCRIPTION_LOG_COMMAND_OUTPUT.get(), Boolean.class, false, 192 false, false); 193 194 195 196 /** 197 * The task property that will be used for the task state for commands that 198 * complete with a nonzero exit code. 199 */ 200 private static final TaskProperty PROPERTY_TASK_STATE_FOR_NONZERO_EXIT_CODE = 201 new TaskProperty(ATTR_TASK_STATE_FOR_NONZERO_EXIT_CODE, 202 INFO_EXEC_DISPLAY_NAME_TASK_STATE_FOR_NONZERO_EXIT_CODE.get(), 203 INFO_EXEC_DESCRIPTION_TASK_STATE_FOR_NONZERO_EXIT_CODE.get(), 204 String.class, false, false, false, 205 new String[] 206 { 207 "STOPPED_BY_ERROR", 208 "STOPPED-BY-ERROR", 209 "COMPLETED_WITH_ERRORS", 210 "COMPLETED-WITH-ERRORS", 211 "COMPLETED_SUCCESSFULLY", 212 "COMPLETED-SUCCESSFULLY" 213 }); 214 215 216 217 /** 218 * The task property that will be used for path to use as the the path to the 219 * working directory to use when executing the command. 220 */ 221 private static final TaskProperty PROPERTY_WORKING_DIRECTORY = 222 new TaskProperty(ATTR_WORKING_DIRECTORY, 223 INFO_EXEC_DISPLAY_NAME_WORKING_DIRECTORY.get(), 224 INFO_EXEC_DESCRIPTION_WORKING_DIRECTORY.get(), 225 String.class, false, false, false); 226 227 228 229 /** 230 * The serial version UID for this serializable class. 231 */ 232 private static final long serialVersionUID = -1647609631634328008L; 233 234 235 236 // Indicates whether command output is to be logged. 237 private final Boolean logCommandOutput; 238 239 // The arguments to provide when executing the command. 240 private final String commandArguments; 241 242 // The path to the file to which command output should be written. 243 private final String commandOutputFile; 244 245 // The path to the command to be executed. 246 private final String commandPath; 247 248 // The name of the task state that should be used if the command completes 249 // with a nonzero exit code. 250 private final String taskStateForNonZeroExitCode; 251 252 // The path to the working directory to use when executing the command. 253 private final String workingDirectory; 254 255 256 257 /** 258 * Creates a new, uninitialized exec task instance that should only be used 259 * for obtaining general information about this task, including the task name, 260 * description, and supported properties. Attempts to use a task created with 261 * this constructor for any other reason will likely fail. 262 */ 263 public ExecTask() 264 { 265 commandPath = null; 266 commandArguments = null; 267 commandOutputFile = null; 268 logCommandOutput = null; 269 taskStateForNonZeroExitCode = null; 270 workingDirectory = null; 271 } 272 273 274 275 /** 276 * Creates a new exec task with the provided information. 277 * 278 * @param commandPath 279 * The absolute path (on the server filesystem) to the command 280 * that should be executed. This must not be {@code null}. 281 * @param commandArguments 282 * The complete set of arguments that should be used when 283 * running the command. This may be {@code null} if no arguments 284 * should be provided. 285 * @param commandOutputFile 286 * The path to an output file that should be used to record all 287 * output that the command writes to standard output or standard 288 * error. This may be {@code null} if the command output should 289 * not be recorded in a file. 290 * @param logCommandOutput 291 * Indicates whether to record the command output in the server 292 * error log. If this is {@code true}, then all non-blank lines 293 * that the command writes to standard output or standard error 294 * will be recorded in the server error log. if this is 295 * {@code false}, then the output will not be recorded in the 296 * server error log. If this is {@code null}, then the server 297 * will determine whether to log command output. Note that a 298 * value of {@code true} should only be used if you are certain 299 * that the tool will only generate text-based output, and you 300 * should use {@code false} if you know that the command may 301 * generate non-text output. 302 * @param taskStateForNonZeroExitCode 303 * The task state that should be used if the command completes 304 * with a nonzero exit code. This may be {@code null} to 305 * indicate that the server should determine the appropriate task 306 * state. If it is non-{@code null}, then the value must be one 307 * of {@link TaskState#STOPPED_BY_ERROR}, 308 * {@link TaskState#COMPLETED_WITH_ERRORS}, or 309 * {@link TaskState#COMPLETED_SUCCESSFULLY}. 310 * 311 * @throws TaskException If there is a problem with any of the provided 312 * arguments. 313 */ 314 public ExecTask(final String commandPath, final String commandArguments, 315 final String commandOutputFile, 316 final Boolean logCommandOutput, 317 final TaskState taskStateForNonZeroExitCode) 318 throws TaskException 319 { 320 this(null, commandPath, commandArguments, commandOutputFile, 321 logCommandOutput, taskStateForNonZeroExitCode, null, null, null, null, 322 null); 323 } 324 325 326 327 /** 328 * Creates a new exec task with the provided information. 329 * 330 * @param commandPath 331 * The absolute path (on the server filesystem) to the command 332 * that should be executed. This must not be {@code null}. 333 * @param commandArguments 334 * The complete set of arguments that should be used when 335 * running the command. This may be {@code null} if no arguments 336 * should be provided. 337 * @param commandOutputFile 338 * The path to an output file that should be used to record all 339 * output that the command writes to standard output or standard 340 * error. This may be {@code null} if the command output should 341 * not be recorded in a file. 342 * @param logCommandOutput 343 * Indicates whether to record the command output in the server 344 * error log. If this is {@code true}, then all non-blank lines 345 * that the command writes to standard output or standard error 346 * will be recorded in the server error log. if this is 347 * {@code false}, then the output will not be recorded in the 348 * server error log. If this is {@code null}, then the server 349 * will determine whether to log command output. Note that a 350 * value of {@code true} should only be used if you are certain 351 * that the tool will only generate text-based output, and you 352 * should use {@code false} if you know that the command may 353 * generate non-text output. 354 * @param taskStateForNonZeroExitCode 355 * The task state that should be used if the command completes 356 * with a nonzero exit code. This may be {@code null} to 357 * indicate that the server should determine the appropriate task 358 * state. If it is non-{@code null}, then the value must be one 359 * of {@link TaskState#STOPPED_BY_ERROR}, 360 * {@link TaskState#COMPLETED_WITH_ERRORS}, or 361 * {@link TaskState#COMPLETED_SUCCESSFULLY}. 362 * @param workingDirectory 363 * The path to the working directory to use when executing the 364 * command. 365 * 366 * @throws TaskException If there is a problem with any of the provided 367 * arguments. 368 */ 369 public ExecTask(final String commandPath, final String commandArguments, 370 final String commandOutputFile, 371 final Boolean logCommandOutput, 372 final TaskState taskStateForNonZeroExitCode, 373 final String workingDirectory) 374 throws TaskException 375 { 376 this(null, commandPath, commandArguments, commandOutputFile, 377 logCommandOutput, taskStateForNonZeroExitCode, workingDirectory, null, 378 null, null, null, null, null, null, null, null, null); 379 } 380 381 382 383 /** 384 * Creates a new exec task with the provided information. 385 * 386 * @param taskID 387 * The task ID to use for this task. If it is {@code null} then 388 * a UUID will be generated for use as the task ID. 389 * @param commandPath 390 * The absolute path (on the server filesystem) to the command 391 * that should be executed. This must not be {@code null}. 392 * @param commandArguments 393 * The complete set of arguments that should be used when 394 * running the command. This may be {@code null} if no arguments 395 * should be provided. 396 * @param commandOutputFile 397 * The path to an output file that should be used to record all 398 * output that the command writes to standard output or standard 399 * error. This may be {@code null} if the command output should 400 * not be recorded in a file. 401 * @param logCommandOutput 402 * Indicates whether to record the command output in the server 403 * error log. If this is {@code true}, then all non-blank lines 404 * that the command writes to standard output or standard error 405 * will be recorded in the server error log. if this is 406 * {@code false}, then the output will not be recorded in the 407 * server error log. If this is {@code null}, then the server 408 * will determine whether to log command output. Note that a 409 * value of {@code true} should only be used if you are certain 410 * that the tool will only generate text-based output, and you 411 * should use {@code false} if you know that the command may 412 * generate non-text output. 413 * @param taskStateForNonZeroExitCode 414 * The task state that should be used if the command completes 415 * with a nonzero exit code. This may be {@code null} to 416 * indicate that the server should determine the appropriate task 417 * state. If it is non-{@code null}, then the value must be one 418 * of {@link TaskState#STOPPED_BY_ERROR}, 419 * {@link TaskState#COMPLETED_WITH_ERRORS}, or 420 * {@link TaskState#COMPLETED_SUCCESSFULLY}. 421 * @param scheduledStartTime 422 * The time that this task should start running. 423 * @param dependencyIDs 424 * The list of task IDs that will be required to complete before 425 * this task will be eligible to start. 426 * @param failedDependencyAction 427 * Indicates what action should be taken if any of the 428 * dependencies for this task do not complete successfully. 429 * @param notifyOnCompletion 430 * The list of e-mail addresses of individuals that should be 431 * notified when this task completes. 432 * @param notifyOnError 433 * The list of e-mail addresses of individuals that should be 434 * notified if this task does not complete successfully. 435 * 436 * @throws TaskException If there is a problem with any of the provided 437 * arguments. 438 */ 439 public ExecTask(final String taskID, final String commandPath, 440 final String commandArguments, final String commandOutputFile, 441 final Boolean logCommandOutput, 442 final TaskState taskStateForNonZeroExitCode, 443 final Date scheduledStartTime, 444 final List<String> dependencyIDs, 445 final FailedDependencyAction failedDependencyAction, 446 final List<String> notifyOnCompletion, 447 final List<String> notifyOnError) 448 throws TaskException 449 { 450 this(taskID, commandPath, commandArguments, commandOutputFile, 451 logCommandOutput, taskStateForNonZeroExitCode, scheduledStartTime, 452 dependencyIDs, failedDependencyAction, null, notifyOnCompletion, 453 null, notifyOnError, null, null, null); 454 } 455 456 457 458 /** 459 * Creates a new exec task with the provided information. 460 * 461 * @param taskID 462 * The task ID to use for this task. If it is {@code null} then 463 * a UUID will be generated for use as the task ID. 464 * @param commandPath 465 * The absolute path (on the server filesystem) to the command 466 * that should be executed. This must not be {@code null}. 467 * @param commandArguments 468 * The complete set of arguments that should be used when 469 * running the command. This may be {@code null} if no arguments 470 * should be provided. 471 * @param commandOutputFile 472 * The path to an output file that should be used to record all 473 * output that the command writes to standard output or standard 474 * error. This may be {@code null} if the command output should 475 * not be recorded in a file. 476 * @param logCommandOutput 477 * Indicates whether to record the command output in the server 478 * error log. If this is {@code true}, then all non-blank lines 479 * that the command writes to standard output or standard error 480 * will be recorded in the server error log. if this is 481 * {@code false}, then the output will not be recorded in the 482 * server error log. If this is {@code null}, then the server 483 * will determine whether to log command output. Note that a 484 * value of {@code true} should only be used if you are certain 485 * that the tool will only generate text-based output, and you 486 * should use {@code false} if you know that the command may 487 * generate non-text output. 488 * @param taskStateForNonZeroExitCode 489 * The task state that should be used if the command completes 490 * with a nonzero exit code. This may be {@code null} to 491 * indicate that the server should determine the appropriate task 492 * state. If it is non-{@code null}, then the value must be one 493 * of {@link TaskState#STOPPED_BY_ERROR}, 494 * {@link TaskState#COMPLETED_WITH_ERRORS}, or 495 * {@link TaskState#COMPLETED_SUCCESSFULLY}. 496 * @param scheduledStartTime 497 * The time that this task should start running. 498 * @param dependencyIDs 499 * The list of task IDs that will be required to complete before 500 * this task will be eligible to start. 501 * @param failedDependencyAction 502 * Indicates what action should be taken if any of the 503 * dependencies for this task do not complete successfully. 504 * @param notifyOnStart 505 * The list of e-mail addresses of individuals that should be 506 * notified when this task starts. 507 * @param notifyOnCompletion 508 * The list of e-mail addresses of individuals that should be 509 * notified when this task completes. 510 * @param notifyOnSuccess 511 * The list of e-mail addresses of individuals that should be 512 * notified if this task completes successfully. 513 * @param notifyOnError 514 * The list of e-mail addresses of individuals that should be 515 * notified if this task does not complete successfully. 516 * @param alertOnStart 517 * Indicates whether the server should send an alert notification 518 * when this task starts. 519 * @param alertOnSuccess 520 * Indicates whether the server should send an alert notification 521 * if this task completes successfully. 522 * @param alertOnError 523 * Indicates whether the server should send an alert notification 524 * if this task fails to complete successfully. 525 * 526 * @throws TaskException If there is a problem with any of the provided 527 * arguments. 528 */ 529 public ExecTask(final String taskID, final String commandPath, 530 final String commandArguments, final String commandOutputFile, 531 final Boolean logCommandOutput, 532 final TaskState taskStateForNonZeroExitCode, 533 final Date scheduledStartTime, 534 final List<String> dependencyIDs, 535 final FailedDependencyAction failedDependencyAction, 536 final List<String> notifyOnStart, 537 final List<String> notifyOnCompletion, 538 final List<String> notifyOnSuccess, 539 final List<String> notifyOnError, final Boolean alertOnStart, 540 final Boolean alertOnSuccess, final Boolean alertOnError) 541 throws TaskException 542 { 543 this(taskID, commandPath, commandArguments, commandOutputFile, 544 logCommandOutput, taskStateForNonZeroExitCode, null, 545 scheduledStartTime, dependencyIDs, failedDependencyAction, 546 notifyOnStart, notifyOnCompletion, notifyOnSuccess, notifyOnError, 547 alertOnStart, alertOnSuccess, alertOnError); 548 } 549 550 551 552 /** 553 * Creates a new exec task with the provided information. 554 * 555 * @param taskID 556 * The task ID to use for this task. If it is {@code null} then 557 * a UUID will be generated for use as the task ID. 558 * @param commandPath 559 * The absolute path (on the server filesystem) to the command 560 * that should be executed. This must not be {@code null}. 561 * @param commandArguments 562 * The complete set of arguments that should be used when 563 * running the command. This may be {@code null} if no arguments 564 * should be provided. 565 * @param commandOutputFile 566 * The path to an output file that should be used to record all 567 * output that the command writes to standard output or standard 568 * error. This may be {@code null} if the command output should 569 * not be recorded in a file. 570 * @param logCommandOutput 571 * Indicates whether to record the command output in the server 572 * error log. If this is {@code true}, then all non-blank lines 573 * that the command writes to standard output or standard error 574 * will be recorded in the server error log. if this is 575 * {@code false}, then the output will not be recorded in the 576 * server error log. If this is {@code null}, then the server 577 * will determine whether to log command output. Note that a 578 * value of {@code true} should only be used if you are certain 579 * that the tool will only generate text-based output, and you 580 * should use {@code false} if you know that the command may 581 * generate non-text output. 582 * @param taskStateForNonZeroExitCode 583 * The task state that should be used if the command completes 584 * with a nonzero exit code. This may be {@code null} to 585 * indicate that the server should determine the appropriate task 586 * state. If it is non-{@code null}, then the value must be one 587 * of {@link TaskState#STOPPED_BY_ERROR}, 588 * {@link TaskState#COMPLETED_WITH_ERRORS}, or 589 * {@link TaskState#COMPLETED_SUCCESSFULLY}. 590 * @param workingDirectory 591 * The path to the working directory to use when executing the 592 * command. 593 * @param scheduledStartTime 594 * The time that this task should start running. 595 * @param dependencyIDs 596 * The list of task IDs that will be required to complete before 597 * this task will be eligible to start. 598 * @param failedDependencyAction 599 * Indicates what action should be taken if any of the 600 * dependencies for this task do not complete successfully. 601 * @param notifyOnStart 602 * The list of e-mail addresses of individuals that should be 603 * notified when this task starts. 604 * @param notifyOnCompletion 605 * The list of e-mail addresses of individuals that should be 606 * notified when this task completes. 607 * @param notifyOnSuccess 608 * The list of e-mail addresses of individuals that should be 609 * notified if this task completes successfully. 610 * @param notifyOnError 611 * The list of e-mail addresses of individuals that should be 612 * notified if this task does not complete successfully. 613 * @param alertOnStart 614 * Indicates whether the server should send an alert notification 615 * when this task starts. 616 * @param alertOnSuccess 617 * Indicates whether the server should send an alert notification 618 * if this task completes successfully. 619 * @param alertOnError 620 * Indicates whether the server should send an alert notification 621 * if this task fails to complete successfully. 622 * 623 * @throws TaskException If there is a problem with any of the provided 624 * arguments. 625 */ 626 public ExecTask(final String taskID, final String commandPath, 627 final String commandArguments, final String commandOutputFile, 628 final Boolean logCommandOutput, 629 final TaskState taskStateForNonZeroExitCode, 630 final String workingDirectory, final Date scheduledStartTime, 631 final List<String> dependencyIDs, 632 final FailedDependencyAction failedDependencyAction, 633 final List<String> notifyOnStart, 634 final List<String> notifyOnCompletion, 635 final List<String> notifyOnSuccess, 636 final List<String> notifyOnError, final Boolean alertOnStart, 637 final Boolean alertOnSuccess, final Boolean alertOnError) 638 throws TaskException 639 { 640 super(taskID, EXEC_TASK_CLASS, scheduledStartTime, dependencyIDs, 641 failedDependencyAction, notifyOnStart, notifyOnCompletion, 642 notifyOnSuccess, notifyOnError, alertOnStart, alertOnSuccess, 643 alertOnError); 644 645 this.commandPath = commandPath; 646 this.commandArguments = commandArguments; 647 this.commandOutputFile = commandOutputFile; 648 this.logCommandOutput = logCommandOutput; 649 this.workingDirectory = workingDirectory; 650 651 if ((commandPath == null) || commandPath.isEmpty()) 652 { 653 throw new TaskException(ERR_EXEC_MISSING_PATH.get()); 654 } 655 656 if (taskStateForNonZeroExitCode == null) 657 { 658 this.taskStateForNonZeroExitCode = null; 659 } 660 else 661 { 662 switch (taskStateForNonZeroExitCode) 663 { 664 case STOPPED_BY_ERROR: 665 case COMPLETED_WITH_ERRORS: 666 case COMPLETED_SUCCESSFULLY: 667 this.taskStateForNonZeroExitCode = taskStateForNonZeroExitCode.name(); 668 break; 669 default: 670 throw new TaskException( 671 ERR_EXEC_INVALID_STATE_FOR_NONZERO_EXIT_CODE.get( 672 TaskState.STOPPED_BY_ERROR.name(), 673 TaskState.COMPLETED_WITH_ERRORS.name(), 674 TaskState.COMPLETED_SUCCESSFULLY.name())); 675 } 676 } 677 } 678 679 680 681 /** 682 * Creates a new exec task from the provided entry. 683 * 684 * @param entry The entry to use to create this exec task. 685 * 686 * @throws TaskException If the provided entry cannot be parsed as an exec 687 * task entry. 688 */ 689 public ExecTask(final Entry entry) 690 throws TaskException 691 { 692 super(entry); 693 694 695 // Get the command to execute. It must be provided. 696 commandPath = entry.getAttributeValue(ATTR_COMMAND_PATH); 697 if (commandPath == null) 698 { 699 throw new TaskException(ERR_EXEC_ENTRY_MISSING_COMMAND_PATH.get( 700 entry.getDN(), ATTR_COMMAND_PATH)); 701 } 702 703 commandArguments = entry.getAttributeValue(ATTR_COMMAND_ARGUMENTS); 704 commandOutputFile = entry.getAttributeValue(ATTR_COMMAND_OUTPUT_FILE); 705 logCommandOutput = 706 entry.getAttributeValueAsBoolean(ATTR_LOG_COMMAND_OUTPUT); 707 taskStateForNonZeroExitCode = 708 entry.getAttributeValue(ATTR_TASK_STATE_FOR_NONZERO_EXIT_CODE); 709 workingDirectory = entry.getAttributeValue(ATTR_WORKING_DIRECTORY); 710 } 711 712 713 714 /** 715 * Creates a new exec task from the provided set of task properties. 716 * 717 * @param properties The set of task properties and their corresponding 718 * values to use for the task. It must not be 719 * {@code null}. 720 * 721 * @throws TaskException If the provided set of properties cannot be used to 722 * create a valid exec task. 723 */ 724 public ExecTask(final Map<TaskProperty,List<Object>> properties) 725 throws TaskException 726 { 727 super(EXEC_TASK_CLASS, properties); 728 729 String path = null; 730 String arguments = null; 731 String outputFile = null; 732 Boolean logOutput = null; 733 String nonZeroExitState = null; 734 String workingDir = null; 735 for (final Map.Entry<TaskProperty,List<Object>> entry : 736 properties.entrySet()) 737 { 738 final TaskProperty p = entry.getKey(); 739 final String attrName = StaticUtils.toLowerCase(p.getAttributeName()); 740 final List<Object> values = entry.getValue(); 741 742 if (attrName.equals(ATTR_COMMAND_PATH)) 743 { 744 path = parseString(p, values, path); 745 } 746 else if (attrName.equals(ATTR_COMMAND_ARGUMENTS)) 747 { 748 arguments = parseString(p, values, arguments); 749 } 750 else if (attrName.equals(ATTR_COMMAND_OUTPUT_FILE)) 751 { 752 outputFile = parseString(p, values, outputFile); 753 } 754 else if (attrName.equals(ATTR_LOG_COMMAND_OUTPUT)) 755 { 756 logOutput = parseBoolean(p, values, logOutput); 757 } 758 else if (attrName.equals(ATTR_TASK_STATE_FOR_NONZERO_EXIT_CODE)) 759 { 760 nonZeroExitState = parseString(p, values, nonZeroExitState); 761 } 762 else if (attrName.equals(ATTR_WORKING_DIRECTORY)) 763 { 764 workingDir = parseString(p, values, workingDir); 765 } 766 } 767 768 commandPath = path; 769 commandArguments = arguments; 770 commandOutputFile = outputFile; 771 logCommandOutput = logOutput; 772 taskStateForNonZeroExitCode = nonZeroExitState; 773 workingDirectory = workingDir; 774 775 if (commandPath == null) 776 { 777 throw new TaskException(ERR_EXEC_PROPERTIES_MISSING_COMMAND_PATH.get()); 778 } 779 } 780 781 782 783 /** 784 * {@inheritDoc} 785 */ 786 @Override() 787 public String getTaskName() 788 { 789 return INFO_TASK_NAME_EXEC.get(); 790 } 791 792 793 794 /** 795 * {@inheritDoc} 796 */ 797 @Override() 798 public String getTaskDescription() 799 { 800 return INFO_TASK_DESCRIPTION_EXEC.get(); 801 } 802 803 804 805 /** 806 * Retrieves the path to the command to be executed. 807 * 808 * @return The path to the command to be executed. 809 */ 810 public String getCommandPath() 811 { 812 return commandPath; 813 } 814 815 816 817 /** 818 * Retrieves a string with the values of the arguments that should be provided 819 * when running the command. 820 * 821 * @return A string with the values of the arguments that should be provided 822 * when running the command, or {@code null} if the command should be 823 * run without any arguments. 824 */ 825 public String getCommandArguments() 826 { 827 return commandArguments; 828 } 829 830 831 832 /** 833 * Retrieves the path to a file to which the command's output should be 834 * written. 835 * 836 * @return The path to a file to which the command's output should be 837 * written, or {@code null} if the output should not be written to a 838 * file. 839 */ 840 public String getCommandOutputFile() 841 { 842 return commandOutputFile; 843 } 844 845 846 847 /** 848 * Indicates whether the command's output should be recorded in the server's 849 * error log. 850 * 851 * @return {@code true} if the command's output should be recorded in the 852 * server's error log, {@code false} if the output should not be 853 * logged, or {@code null} if the task should not specify the 854 * behavior. 855 */ 856 public Boolean logCommandOutput() 857 { 858 return logCommandOutput; 859 } 860 861 862 863 /** 864 * Retrieves a string representation of the task state that should be returned 865 * if the command completes with a nonzero exit code. 866 * 867 * @return A string representation of the task state that should be returned 868 * if the command completes with a nonzero exit state, or 869 * {@code null} if the task should not specify the return state. 870 */ 871 public String getTaskStateForNonZeroExitCode() 872 { 873 return taskStateForNonZeroExitCode; 874 } 875 876 877 878 /** 879 * Retrieves the path to the working directory to use when executing the 880 * command. 881 * 882 * @return The path to the working directory to use when executing the 883 * command, or {@code null} if the task should not specify the 884 * working directory and the server root directory should be used by 885 * default. 886 */ 887 public String getWorkingDirectory() 888 { 889 return workingDirectory; 890 } 891 892 893 894 /** 895 * {@inheritDoc} 896 */ 897 @Override() 898 protected List<String> getAdditionalObjectClasses() 899 { 900 return Collections.singletonList(OC_EXEC_TASK); 901 } 902 903 904 905 /** 906 * {@inheritDoc} 907 */ 908 @Override() 909 protected List<Attribute> getAdditionalAttributes() 910 { 911 final LinkedList<Attribute> attrList = new LinkedList<>(); 912 attrList.add(new Attribute(ATTR_COMMAND_PATH, commandPath)); 913 914 if (commandArguments != null) 915 { 916 attrList.add(new Attribute(ATTR_COMMAND_ARGUMENTS, commandArguments)); 917 } 918 919 if (commandOutputFile != null) 920 { 921 attrList.add(new Attribute(ATTR_COMMAND_OUTPUT_FILE, commandOutputFile)); 922 } 923 924 if (logCommandOutput != null) 925 { 926 attrList.add(new Attribute(ATTR_LOG_COMMAND_OUTPUT, 927 String.valueOf(logCommandOutput))); 928 } 929 930 if (taskStateForNonZeroExitCode != null) 931 { 932 attrList.add(new Attribute(ATTR_TASK_STATE_FOR_NONZERO_EXIT_CODE, 933 taskStateForNonZeroExitCode)); 934 } 935 936 if (workingDirectory != null) 937 { 938 attrList.add(new Attribute(ATTR_WORKING_DIRECTORY, workingDirectory)); 939 } 940 941 return attrList; 942 } 943 944 945 946 /** 947 * {@inheritDoc} 948 */ 949 @Override() 950 public List<TaskProperty> getTaskSpecificProperties() 951 { 952 return Collections.unmodifiableList(Arrays.asList( 953 PROPERTY_COMMAND_PATH, PROPERTY_COMMAND_ARGUMENTS, 954 PROPERTY_COMMAND_OUTPUT_FILE, PROPERTY_LOG_COMMAND_OUTPUT, 955 PROPERTY_TASK_STATE_FOR_NONZERO_EXIT_CODE, 956 PROPERTY_WORKING_DIRECTORY)); 957 } 958 959 960 961 /** 962 * {@inheritDoc} 963 */ 964 @Override() 965 public Map<TaskProperty,List<Object>> getTaskPropertyValues() 966 { 967 final LinkedHashMap<TaskProperty, List<Object>> props = 968 new LinkedHashMap<>(StaticUtils.computeMapCapacity( 969 StaticUtils.computeMapCapacity(6))); 970 971 props.put(PROPERTY_COMMAND_PATH, 972 Collections.<Object>singletonList(commandPath)); 973 974 if (commandArguments != null) 975 { 976 props.put(PROPERTY_COMMAND_ARGUMENTS, 977 Collections.<Object>singletonList(commandArguments)); 978 } 979 980 if (commandOutputFile != null) 981 { 982 props.put(PROPERTY_COMMAND_OUTPUT_FILE, 983 Collections.<Object>singletonList(commandOutputFile)); 984 } 985 986 if (logCommandOutput != null) 987 { 988 props.put(PROPERTY_LOG_COMMAND_OUTPUT, 989 Collections.<Object>singletonList(logCommandOutput)); 990 } 991 992 if (taskStateForNonZeroExitCode != null) 993 { 994 props.put(PROPERTY_TASK_STATE_FOR_NONZERO_EXIT_CODE, 995 Collections.<Object>singletonList(taskStateForNonZeroExitCode)); 996 } 997 998 if (workingDirectory != null) 999 { 1000 props.put(PROPERTY_WORKING_DIRECTORY, 1001 Collections.<Object>singletonList(workingDirectory)); 1002 } 1003 1004 return Collections.unmodifiableMap(props); 1005 } 1006}