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