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