001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2008-2009 Sun Microsystems, Inc. 025 * Portions Copyright 2012-2015 ForgeRock AS. 026 */ 027package org.opends.server.tools; 028 029import org.forgerock.i18n.LocalizableMessage; 030import org.forgerock.opendj.config.server.ConfigException; 031import org.forgerock.opendj.ldap.DecodeException; 032import org.opends.server.backends.task.TaskState; 033import org.opends.server.core.DirectoryServer; 034import org.opends.server.loggers.JDKLogging; 035import org.opends.server.tools.tasks.TaskClient; 036import org.opends.server.tools.tasks.TaskEntry; 037import org.opends.server.types.InitializationException; 038import org.opends.server.types.LDAPException; 039import org.opends.server.util.BuildVersion; 040import org.opends.server.util.StaticUtils; 041import org.opends.server.util.args.LDAPConnectionArgumentParser; 042import org.opends.server.util.cli.LDAPConnectionConsoleInteraction; 043 044import com.forgerock.opendj.cli.ArgumentException; 045import com.forgerock.opendj.cli.BooleanArgument; 046import com.forgerock.opendj.cli.ClientException; 047import com.forgerock.opendj.cli.CommonArguments; 048import com.forgerock.opendj.cli.ConsoleApplication; 049import com.forgerock.opendj.cli.Menu; 050import com.forgerock.opendj.cli.MenuBuilder; 051import com.forgerock.opendj.cli.MenuCallback; 052import com.forgerock.opendj.cli.MenuResult; 053import com.forgerock.opendj.cli.StringArgument; 054import com.forgerock.opendj.cli.TableBuilder; 055import com.forgerock.opendj.cli.TextTablePrinter; 056 057import java.io.IOException; 058import java.io.InputStream; 059import java.io.OutputStream; 060import java.io.PrintStream; 061import java.io.StringWriter; 062import java.util.ArrayList; 063import java.util.List; 064import java.util.Map; 065import java.util.TreeMap; 066 067import static org.opends.messages.ToolMessages.*; 068 069import static com.forgerock.opendj.cli.ArgumentConstants.*; 070import static com.forgerock.opendj.cli.Utils.*; 071 072/** Tool for getting information and managing tasks in the Directory Server. */ 073public class ManageTasks extends ConsoleApplication { 074 /** This CLI is always using the administration connector with SSL. */ 075 private static final boolean alwaysSSL = true; 076 077 /** 078 * The main method for TaskInfo tool. 079 * 080 * @param args The command-line arguments provided to this program. 081 */ 082 public static void main(String[] args) { 083 int retCode = mainTaskInfo(args, System.in, System.out, System.err); 084 085 if (retCode != 0) { 086 System.exit(filterExitCode(retCode)); 087 } 088 } 089 090 /** 091 * Processes the command-line arguments and invokes the process for 092 * displaying task information. 093 * 094 * @param args The command-line arguments provided to this program. 095 * @return int return code 096 */ 097 public static int mainTaskInfo(String[] args) { 098 return mainTaskInfo(args, System.in, System.out, System.err); 099 } 100 101 /** 102 * Processes the command-line arguments and invokes the export process. 103 * 104 * @param args The command-line arguments provided to this 105 * @param in Input stream from which to solicit user input. 106 * @param out The output stream to use for standard output, or 107 * {@code null} if standard output is not needed. 108 * @param err The output stream to use for standard error, or 109 * {@code null} if standard error is not needed. 110 * @param initializeServer Indicates whether to initialize the server. 111 * @return int return code 112 */ 113 public static int mainTaskInfo(String[] args, 114 InputStream in, 115 OutputStream out, 116 OutputStream err, 117 boolean initializeServer) { 118 ManageTasks tool = new ManageTasks(in, out, err); 119 return tool.process(args, initializeServer); 120 } 121 122 /** 123 * Processes the command-line arguments and invokes the export process. 124 * 125 * @param args The command-line arguments provided to this 126 * @param in Input stream from which to solicit user input. 127 * @param out The output stream to use for standard output, or 128 * {@code null} if standard output is not needed. 129 * @param err The output stream to use for standard error, or 130 * {@code null} if standard error is not needed. 131 * @return int return code 132 */ 133 public static int mainTaskInfo(String[] args, 134 InputStream in, 135 OutputStream out, 136 OutputStream err) { 137 return mainTaskInfo(args, in, out, err, true); 138 } 139 140 private static final int INDENT = 2; 141 142 /** ID of task for which to display details and exit. */ 143 private StringArgument task; 144 /** Indicates print summary and exit. */ 145 private BooleanArgument summary; 146 /** ID of task to cancel. */ 147 private StringArgument cancel; 148 /** Argument used to request non-interactive behavior. */ 149 private BooleanArgument noPrompt; 150 151 /** Accesses the directory's task backend. */ 152 private TaskClient taskClient; 153 154 /** 155 * Constructs a parameterized instance. 156 * 157 * @param in Input stream from which to solicit user input. 158 * @param out The output stream to use for standard output, or 159 * {@code null} if standard output is not needed. 160 * @param err The output stream to use for standard error, or 161 * {@code null} if standard error is not needed. 162 */ 163 public ManageTasks(InputStream in, OutputStream out, OutputStream err) 164 { 165 super(new PrintStream(out), new PrintStream(err)); 166 } 167 168 /** 169 * Processes the command-line arguments and invokes the export process. 170 * 171 * @param args The command-line arguments provided to this 172 * program. 173 * @return The error code. 174 */ 175 public int process(String[] args) 176 { 177 return process(args, true); 178 } 179 180 /** 181 * Processes the command-line arguments and invokes the export process. 182 * 183 * @param args The command-line arguments provided to this 184 * program. 185 * @param initializeServer Indicates whether to initialize the server. 186 * @return The error code. 187 */ 188 public int process(String[] args, boolean initializeServer) 189 { 190 if (initializeServer) 191 { 192 DirectoryServer.bootstrapClient(); 193 } 194 JDKLogging.disableLogging(); 195 196 // Create the command-line argument parser for use with this program. 197 LDAPConnectionArgumentParser argParser = new LDAPConnectionArgumentParser( 198 "org.opends.server.tools.TaskInfo", 199 INFO_TASKINFO_TOOL_DESCRIPTION.get(), 200 false, null, alwaysSSL); 201 argParser.setShortToolDescription(REF_SHORT_DESC_MANAGE_TASKS.get()); 202 203 // Initialize all the command-line argument types and register them with the parser 204 try { 205 StringArgument propertiesFileArgument = new StringArgument( 206 "propertiesFilePath", null, OPTION_LONG_PROP_FILE_PATH, false, false, 207 true, INFO_PROP_FILE_PATH_PLACEHOLDER.get(), null, null, 208 INFO_DESCRIPTION_PROP_FILE_PATH.get()); 209 argParser.addArgument(propertiesFileArgument); 210 argParser.setFilePropertiesArgument(propertiesFileArgument); 211 212 BooleanArgument noPropertiesFileArgument = new BooleanArgument( 213 "noPropertiesFileArgument", null, OPTION_LONG_NO_PROP_FILE, 214 INFO_DESCRIPTION_NO_PROP_FILE.get()); 215 argParser.addArgument(noPropertiesFileArgument); 216 argParser.setNoPropertiesFileArgument(noPropertiesFileArgument); 217 218 task = new StringArgument( 219 "info", 'i', "info", 220 false, true, INFO_TASK_ID_PLACEHOLDER.get(), 221 INFO_TASKINFO_TASK_ARG_DESCRIPTION.get()); 222 argParser.addArgument(task); 223 224 cancel = new StringArgument( 225 "cancel", 'c', "cancel", 226 false, true, INFO_TASK_ID_PLACEHOLDER.get(), 227 INFO_TASKINFO_TASK_ARG_CANCEL.get()); 228 argParser.addArgument(cancel); 229 230 summary = new BooleanArgument( 231 "summary", 's', "summary", 232 INFO_TASKINFO_SUMMARY_ARG_DESCRIPTION.get()); 233 argParser.addArgument(summary); 234 235 noPrompt = CommonArguments.getNoPrompt(); 236 argParser.addArgument(noPrompt); 237 238 BooleanArgument displayUsage = CommonArguments.getShowUsage(); 239 argParser.addArgument(displayUsage); 240 argParser.setUsageArgument(displayUsage); 241 } 242 catch (ArgumentException ae) { 243 LocalizableMessage message = ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage()); 244 println(message); 245 return 1; 246 } 247 248 try 249 { 250 argParser.getArguments().initArgumentsWithConfiguration(); 251 } 252 catch (ConfigException ignored) {} 253 254 // Parse the command-line arguments provided to this program. 255 try { 256 argParser.parseArguments(args); 257 StaticUtils.checkOnlyOneArgPresent(task, summary, cancel); 258 } 259 catch (ArgumentException ae) { 260 argParser.displayMessageAndUsageReference(getErrStream(), ERR_ERROR_PARSING_ARGS.get(ae.getMessage())); 261 return 1; 262 } 263 264 if (!argParser.usageOrVersionDisplayed()) { 265 // Checks the version - if upgrade required, the tool is unusable 266 try 267 { 268 BuildVersion.checkVersionMismatch(); 269 } 270 catch (InitializationException e) 271 { 272 println(e.getMessageObject()); 273 return 1; 274 } 275 276 try { 277 LDAPConnectionConsoleInteraction ui = 278 new LDAPConnectionConsoleInteraction( 279 this, argParser.getArguments()); 280 281 taskClient = new TaskClient(argParser.connect(ui, 282 getOutputStream(), getErrorStream())); 283 284 if (isMenuDrivenMode()) { 285 // Keep prompting the user until they specify quit of there is a fatal exception 286 while (true) { 287 getOutputStream().println(); 288 Menu<Void> menu = getSummaryMenu(); 289 MenuResult<Void> result = menu.run(); 290 if (result.isQuit()) { 291 return 0; 292 } 293 } 294 } else if (task.isPresent()) { 295 getOutputStream().println(); 296 MenuResult<TaskEntry> r = new PrintTaskInfo(task.getValue()).invoke(this); 297 if (r.isAgain()) 298 { 299 return 1; 300 } 301 } else if (summary.isPresent()) { 302 getOutputStream().println(); 303 printSummaryTable(); 304 } else if (cancel.isPresent()) { 305 MenuResult<TaskEntry> r = new CancelTask(cancel.getValue()).invoke(this); 306 if (r.isAgain()) 307 { 308 return 1; 309 } 310 } else if (!isInteractive()) { 311 // no-prompt option 312 getOutputStream().println(); 313 printSummaryTable(); 314 return 0; 315 } 316 } catch (LDAPConnectionException lce) { 317 println(INFO_TASKINFO_LDAP_EXCEPTION.get(lce.getMessageObject())); 318 return 1; 319 } catch (Exception e) { 320 println(LocalizableMessage.raw(StaticUtils.getExceptionMessage(e))); 321 return 1; 322 } 323 } 324 return 0; 325 } 326 327 @Override 328 public boolean isAdvancedMode() { 329 return false; 330 } 331 332 @Override 333 public boolean isInteractive() { 334 return !noPrompt.isPresent(); 335 } 336 337 @Override 338 public boolean isMenuDrivenMode() { 339 return !task.isPresent() && !cancel.isPresent() && !summary.isPresent() && !noPrompt.isPresent(); 340 } 341 342 @Override 343 public boolean isQuiet() { 344 return false; 345 } 346 347 @Override 348 public boolean isScriptFriendly() { 349 return false; 350 } 351 352 @Override 353 public boolean isVerbose() { 354 return false; 355 } 356 357 /** 358 * Creates the summary table. 359 * 360 * @throws IOException if there is a problem with screen I/O 361 * @throws LDAPException if there is a problem getting information 362 * out to the directory 363 * @throws DecodeException if there is a problem with the encoding 364 */ 365 private void printSummaryTable() 366 throws LDAPException, IOException, DecodeException { 367 List<TaskEntry> entries = taskClient.getTaskEntries(); 368 if (!entries.isEmpty()) { 369 TableBuilder table = new TableBuilder(); 370 Map<String, TaskEntry> mapIdToEntry = new TreeMap<>(); 371 for (TaskEntry entry : entries) { 372 String taskId = entry.getId(); 373 if (taskId != null) { 374 mapIdToEntry.put(taskId, entry); 375 } 376 } 377 378 table.appendHeading(INFO_TASKINFO_FIELD_ID.get()); 379 table.appendHeading(INFO_TASKINFO_FIELD_TYPE.get()); 380 table.appendHeading(INFO_TASKINFO_FIELD_STATUS.get()); 381 for (String taskId : mapIdToEntry.keySet()) { 382 TaskEntry entryWrapper = mapIdToEntry.get(taskId); 383 table.startRow(); 384 table.appendCell(taskId); 385 table.appendCell(entryWrapper.getType()); 386 table.appendCell(entryWrapper.getState()); 387 } 388 StringWriter sw = new StringWriter(); 389 TextTablePrinter tablePrinter = new TextTablePrinter(sw); 390 tablePrinter.setIndentWidth(INDENT); 391 tablePrinter.setTotalWidth(80); 392 table.print(tablePrinter); 393 getOutputStream().println(LocalizableMessage.raw(sw.getBuffer())); 394 } else { 395 getOutputStream().println(INFO_TASKINFO_NO_TASKS.get()); 396 getOutputStream().println(); 397 } 398 } 399 400 /** 401 * Creates the summary table. 402 * 403 * @return list of strings of IDs of all the tasks in the table in order 404 * of the indexes printed in the table 405 * @throws IOException if there is a problem with screen I/O 406 * @throws LDAPException if there is a problem getting information 407 * out to the directory 408 * @throws DecodeException if there is a problem with the encoding 409 */ 410 private Menu<Void> getSummaryMenu() 411 throws LDAPException, IOException, DecodeException { 412 List<String> taskIds = new ArrayList<>(); 413 List<Integer> cancelableIndices = new ArrayList<>(); 414 List<TaskEntry> entries = taskClient.getTaskEntries(); 415 MenuBuilder<Void> menuBuilder = new MenuBuilder<>(this); 416 if (!entries.isEmpty()) { 417 Map<String, TaskEntry> mapIdToEntry = new TreeMap<>(); 418 for (TaskEntry entry : entries) { 419 String taskId = entry.getId(); 420 if (taskId != null) { 421 mapIdToEntry.put(taskId, entry); 422 } 423 } 424 425 menuBuilder.setColumnHeadings( 426 INFO_TASKINFO_FIELD_ID.get(), 427 INFO_TASKINFO_FIELD_TYPE.get(), 428 INFO_TASKINFO_FIELD_STATUS.get()); 429 menuBuilder.setColumnWidths(null, null, 0); 430 int index = 0; 431 for (final String taskId : mapIdToEntry.keySet()) { 432 taskIds.add(taskId); 433 final TaskEntry taskEntry = mapIdToEntry.get(taskId); 434 menuBuilder.addNumberedOption( 435 LocalizableMessage.raw(taskEntry.getId()), 436 new TaskDrilldownMenu(taskId), 437 taskEntry.getType(), taskEntry.getState()); 438 index++; 439 if (taskEntry.isCancelable()) { 440 cancelableIndices.add(index); 441 } 442 } 443 } else { 444 getOutputStream().println(INFO_TASKINFO_NO_TASKS.get()); 445 getOutputStream().println(); 446 } 447 448 menuBuilder.addCharOption( 449 INFO_TASKINFO_CMD_REFRESH_CHAR.get(), 450 INFO_TASKINFO_CMD_REFRESH.get(), 451 new PrintSummaryTop()); 452 453 if (!cancelableIndices.isEmpty()) { 454 menuBuilder.addCharOption( 455 INFO_TASKINFO_CMD_CANCEL_CHAR.get(), 456 INFO_TASKINFO_CMD_CANCEL.get(), 457 new CancelTaskTop(taskIds, cancelableIndices)); 458 } 459 menuBuilder.addQuitOption(); 460 461 return menuBuilder.toMenu(); 462 } 463 464 /** 465 * Gets the client that can be used to interact with the task backend. 466 * 467 * @return TaskClient for interacting with the task backend. 468 */ 469 public TaskClient getTaskClient() { 470 return this.taskClient; 471 } 472 473 private static void printTable(TableBuilder table, int column, int width, StringWriter sw) 474 { 475 TextTablePrinter tablePrinter = new TextTablePrinter(sw); 476 tablePrinter.setTotalWidth(80); 477 tablePrinter.setIndentWidth(INDENT); 478 tablePrinter.setColumnWidth(column, width); 479 table.print(tablePrinter); 480 } 481 482 /** Base for callbacks that implement top level menu items. */ 483 private static abstract class TopMenuCallback 484 implements MenuCallback<Void> { 485 @Override 486 public MenuResult<Void> invoke(ConsoleApplication app) throws ClientException { 487 return invoke((ManageTasks)app); 488 } 489 490 /** 491 * Called upon task invocation. 492 * 493 * @param app this console application 494 * @return MessageResult result of task 495 * @throws ClientException if there is a problem 496 */ 497 protected abstract MenuResult<Void> invoke(ManageTasks app) throws ClientException; 498 } 499 500 /** Base for callbacks that manage task entries. */ 501 private static abstract class TaskOperationCallback 502 implements MenuCallback<TaskEntry> { 503 /** ID of the task to manage. */ 504 protected String taskId; 505 506 /** 507 * Constructs a parameterized instance. 508 * 509 * @param taskId if the task to examine 510 */ 511 public TaskOperationCallback(String taskId) { 512 this.taskId = taskId; 513 } 514 515 @Override 516 public MenuResult<TaskEntry> invoke(ConsoleApplication app) throws ClientException 517 { 518 return invoke((ManageTasks)app); 519 } 520 521 /** 522 * Invokes the task. 523 * 524 * @param app 525 * the current application running 526 * @return how the application should proceed next 527 * @throws ClientException 528 * if any problem occurred 529 */ 530 protected abstract MenuResult<TaskEntry> invoke(ManageTasks app) throws ClientException; 531 } 532 533 /** Executable for printing a task summary table. */ 534 private static class PrintSummaryTop extends TopMenuCallback { 535 @Override 536 public MenuResult<Void> invoke(ManageTasks app) throws ClientException 537 { 538 // Since the summary table is reprinted every time, 539 // the user enters the top level this task just returns 'success' 540 return MenuResult.success(); 541 } 542 } 543 544 /** Executable for printing a particular task's details. */ 545 private static class TaskDrilldownMenu extends TopMenuCallback { 546 private String taskId; 547 548 /** 549 * Constructs a parameterized instance. 550 * 551 * @param taskId of the task for which information will be displayed 552 */ 553 public TaskDrilldownMenu(String taskId) { 554 this.taskId = taskId; 555 } 556 557 @Override 558 public MenuResult<Void> invoke(ManageTasks app) throws ClientException { 559 MenuResult<TaskEntry> res = new PrintTaskInfo(taskId).invoke(app); 560 TaskEntry taskEntry = res.getValue(); 561 if (taskEntry != null) { 562 while (true) { 563 try { 564 taskEntry = app.getTaskClient().getTaskEntry(taskId); 565 566 // Show the menu 567 MenuBuilder<TaskEntry> menuBuilder = new MenuBuilder<>(app); 568 menuBuilder.addBackOption(true); 569 menuBuilder.addCharOption( 570 INFO_TASKINFO_CMD_REFRESH_CHAR.get(), 571 INFO_TASKINFO_CMD_REFRESH.get(), 572 new PrintTaskInfo(taskId)); 573 List<LocalizableMessage> logs = taskEntry.getLogMessages(); 574 if (logs != null && !logs.isEmpty()) { 575 menuBuilder.addCharOption( 576 INFO_TASKINFO_CMD_VIEW_LOGS_CHAR.get(), 577 INFO_TASKINFO_CMD_VIEW_LOGS.get(), 578 new ViewTaskLogs(taskId)); 579 } 580 if (taskEntry.isCancelable() && !taskEntry.isDone()) { 581 menuBuilder.addCharOption( 582 INFO_TASKINFO_CMD_CANCEL_CHAR.get(), 583 INFO_TASKINFO_CMD_CANCEL.get(), 584 new CancelTask(taskId)); 585 } 586 menuBuilder.addQuitOption(); 587 Menu<TaskEntry> menu = menuBuilder.toMenu(); 588 MenuResult<TaskEntry> result = menu.run(); 589 if (result.isCancel()) { 590 break; 591 } else if (result.isQuit()) { 592 System.exit(0); 593 } 594 } catch (Exception e) { 595 app.println(LocalizableMessage.raw(StaticUtils.getExceptionMessage(e))); 596 } 597 } 598 } else { 599 app.println(ERR_TASKINFO_UNKNOWN_TASK_ENTRY.get(taskId)); 600 } 601 return MenuResult.success(); 602 } 603 } 604 605 /** Executable for printing a particular task's details. */ 606 private static class PrintTaskInfo extends TaskOperationCallback { 607 /** 608 * Constructs a parameterized instance. 609 * 610 * @param taskId of the task for which information will be printed 611 */ 612 public PrintTaskInfo(String taskId) { 613 super(taskId); 614 } 615 616 @Override 617 public MenuResult<TaskEntry> invoke(ManageTasks app) throws ClientException 618 { 619 try { 620 TaskEntry taskEntry = app.getTaskClient().getTaskEntry(taskId); 621 622 TableBuilder table = new TableBuilder(); 623 table.appendHeading(INFO_TASKINFO_DETAILS.get()); 624 625 table.startRow(); 626 table.appendCell(INFO_TASKINFO_FIELD_ID.get()); 627 table.appendCell(taskEntry.getId()); 628 629 table.startRow(); 630 table.appendCell(INFO_TASKINFO_FIELD_TYPE.get()); 631 table.appendCell(taskEntry.getType()); 632 633 table.startRow(); 634 table.appendCell(INFO_TASKINFO_FIELD_STATUS.get()); 635 table.appendCell(taskEntry.getState()); 636 637 table.startRow(); 638 table.appendCell(INFO_TASKINFO_FIELD_SCHEDULED_START.get()); 639 640 if (TaskState.isRecurring(taskEntry.getTaskState())) { 641 LocalizableMessage m = taskEntry.getScheduleTab(); 642 table.appendCell(m); 643 } else { 644 LocalizableMessage m = taskEntry.getScheduledStartTime(); 645 if (m == null || m.equals(LocalizableMessage.EMPTY)) { 646 table.appendCell(INFO_TASKINFO_IMMEDIATE_EXECUTION.get()); 647 } else { 648 table.appendCell(m); 649 } 650 651 table.startRow(); 652 table.appendCell(INFO_TASKINFO_FIELD_ACTUAL_START.get()); 653 table.appendCell(taskEntry.getActualStartTime()); 654 655 table.startRow(); 656 table.appendCell(INFO_TASKINFO_FIELD_COMPLETION_TIME.get()); 657 table.appendCell(taskEntry.getCompletionTime()); 658 } 659 660 writeMultiValueCells( 661 table, 662 INFO_TASKINFO_FIELD_DEPENDENCY.get(), 663 taskEntry.getDependencyIds()); 664 665 table.startRow(); 666 table.appendCell(INFO_TASKINFO_FIELD_FAILED_DEPENDENCY_ACTION.get()); 667 LocalizableMessage m = taskEntry.getFailedDependencyAction(); 668 table.appendCell(m != null ? m : INFO_TASKINFO_NONE.get()); 669 670 writeMultiValueCells( 671 table, 672 INFO_TASKINFO_FIELD_NOTIFY_ON_COMPLETION.get(), 673 taskEntry.getCompletionNotificationEmailAddresses(), 674 INFO_TASKINFO_NONE_SPECIFIED.get()); 675 676 writeMultiValueCells( 677 table, 678 INFO_TASKINFO_FIELD_NOTIFY_ON_ERROR.get(), 679 taskEntry.getErrorNotificationEmailAddresses(), 680 INFO_TASKINFO_NONE_SPECIFIED.get()); 681 682 StringWriter sw = new StringWriter(); 683 printTable(table, 1, 0, sw); 684 app.getOutputStream().println(); 685 app.getOutputStream().println(LocalizableMessage.raw(sw.getBuffer().toString())); 686 687 // Create a table for the task options 688 table = new TableBuilder(); 689 table.appendHeading(INFO_TASKINFO_OPTIONS.get(taskEntry.getType())); 690 Map<LocalizableMessage,List<String>> taskSpecificAttrs = 691 taskEntry.getTaskSpecificAttributeValuePairs(); 692 for (LocalizableMessage attrName : taskSpecificAttrs.keySet()) { 693 table.startRow(); 694 table.appendCell(attrName); 695 List<String> values = taskSpecificAttrs.get(attrName); 696 if (!values.isEmpty()) { 697 table.appendCell(values.get(0)); 698 } 699 if (values.size() > 1) { 700 for (int i = 1; i < values.size(); i++) { 701 table.startRow(); 702 table.appendCell(); 703 table.appendCell(values.get(i)); 704 } 705 } 706 } 707 sw = new StringWriter(); 708 printTable(table, 1, 0, sw); 709 app.getOutputStream().println(LocalizableMessage.raw(sw.getBuffer().toString())); 710 711 // Print the last log message if any 712 List<LocalizableMessage> logs = taskEntry.getLogMessages(); 713 if (logs != null && !logs.isEmpty()) { 714 // Create a table for the last log entry 715 table = new TableBuilder(); 716 table.appendHeading(INFO_TASKINFO_FIELD_LAST_LOG.get()); 717 table.startRow(); 718 table.appendCell(logs.get(logs.size() - 1)); 719 720 sw = new StringWriter(); 721 printTable(table, 0, 0, sw); 722 app.getOutputStream().println(LocalizableMessage.raw(sw.getBuffer().toString())); 723 } 724 725 app.getOutputStream().println(); 726 return MenuResult.success(taskEntry); 727 } catch (Exception e) { 728 app.errPrintln(ERR_TASKINFO_RETRIEVING_TASK_ENTRY.get(taskId, e.getMessage())); 729 return MenuResult.again(); 730 } 731 } 732 733 /** 734 * Writes an attribute and associated values to the table. 735 * @param table of task details 736 * @param fieldLabel of attribute 737 * @param values of the attribute 738 */ 739 private void writeMultiValueCells(TableBuilder table, 740 LocalizableMessage fieldLabel, 741 List<?> values) { 742 writeMultiValueCells(table, fieldLabel, values, INFO_TASKINFO_NONE.get()); 743 } 744 745 /** 746 * Writes an attribute and associated values to the table. 747 * 748 * @param table of task details 749 * @param fieldLabel of attribute 750 * @param values of the attribute 751 * @param noneLabel label for the value column when there are no values 752 */ 753 private void writeMultiValueCells(TableBuilder table, 754 LocalizableMessage fieldLabel, 755 List<?> values, 756 LocalizableMessage noneLabel) { 757 table.startRow(); 758 table.appendCell(fieldLabel); 759 if (values.isEmpty()) { 760 table.appendCell(noneLabel); 761 } else { 762 table.appendCell(values.get(0)); 763 } 764 if (values.size() > 1) { 765 for (int i = 1; i < values.size(); i++) { 766 table.startRow(); 767 table.appendCell(); 768 table.appendCell(values.get(i)); 769 } 770 } 771 } 772 } 773 774 /** Executable for printing a particular task's details. */ 775 private static class ViewTaskLogs extends TaskOperationCallback { 776 /** 777 * Constructs a parameterized instance. 778 * 779 * @param taskId of the task for which log records will be printed 780 */ 781 public ViewTaskLogs(String taskId) { 782 super(taskId); 783 } 784 785 @Override 786 protected MenuResult<TaskEntry> invoke(ManageTasks app) throws ClientException 787 { 788 TaskEntry taskEntry = null; 789 try { 790 taskEntry = app.getTaskClient().getTaskEntry(taskId); 791 List<LocalizableMessage> logs = taskEntry.getLogMessages(); 792 app.getOutputStream().println(); 793 794 // Create a table for the last log entry 795 TableBuilder table = new TableBuilder(); 796 table.appendHeading(INFO_TASKINFO_FIELD_LOG.get()); 797 if (logs != null && !logs.isEmpty()) { 798 for (LocalizableMessage log : logs) { 799 table.startRow(); 800 table.appendCell(log); 801 } 802 } else { 803 table.startRow(); 804 table.appendCell(INFO_TASKINFO_NONE.get()); 805 } 806 StringWriter sw = new StringWriter(); 807 printTable(table, 0, 0, sw); 808 app.getOutputStream().println(LocalizableMessage.raw(sw.getBuffer().toString())); 809 app.getOutputStream().println(); 810 } catch (Exception e) { 811 app.println(ERR_TASKINFO_ACCESSING_LOGS.get(taskId, e.getMessage())); 812 } 813 return MenuResult.success(taskEntry); 814 } 815 } 816 817 /** Executable for canceling a particular task. */ 818 private static class CancelTaskTop extends TopMenuCallback { 819 private List<String> taskIds; 820 private List<Integer> cancelableIndices; 821 822 /** 823 * Constructs a parameterized instance. 824 * 825 * @param taskIds of all known tasks 826 * @param cancelableIndices list of integers whose elements represent 827 * the indices of <code>taskIds</code> that are cancelable 828 */ 829 public CancelTaskTop(List<String> taskIds, List<Integer> cancelableIndices) 830 { 831 this.taskIds = taskIds; 832 this.cancelableIndices = cancelableIndices; 833 } 834 835 @Override 836 public MenuResult<Void> invoke(ManageTasks app) throws ClientException 837 { 838 if (taskIds == null || taskIds.isEmpty()) { 839 app.println(INFO_TASKINFO_NO_TASKS.get()); 840 return MenuResult.cancel(); 841 } 842 if (cancelableIndices == null || cancelableIndices.isEmpty()) { 843 app.println(INFO_TASKINFO_NO_CANCELABLE_TASKS.get()); 844 return MenuResult.cancel(); 845 } 846 847 // Prompt for the task number 848 Integer index = null; 849 String line = app.readLineOfInput(INFO_TASKINFO_CMD_CANCEL_NUMBER_PROMPT.get(cancelableIndices.get(0))); 850 if (line.length() == 0) { 851 line = String.valueOf(cancelableIndices.get(0)); 852 } 853 854 try { 855 int i = Integer.parseInt(line); 856 if (!cancelableIndices.contains(i)) { 857 app.println(ERR_TASKINFO_NOT_CANCELABLE_TASK_INDEX.get(i)); 858 } else { 859 index = i - 1; 860 } 861 } catch (NumberFormatException ignored) {} 862 863 if (index == null) { 864 app.errPrintln(ERR_TASKINFO_INVALID_MENU_KEY.get(line)); 865 return MenuResult.again(); 866 } 867 868 String taskId = taskIds.get(index); 869 try { 870 CancelTask ct = new CancelTask(taskId); 871 MenuResult<TaskEntry> result = ct.invoke(app); 872 return result.isSuccess() ? MenuResult.<Void> success() : MenuResult.<Void> again(); 873 } catch (Exception e) { 874 app.errPrintln(ERR_TASKINFO_CANCELING_TASK.get(taskId, e.getMessage())); 875 return MenuResult.again(); 876 } 877 } 878 } 879 880 /** Executable for canceling a particular task. */ 881 private static class CancelTask extends TaskOperationCallback { 882 /** 883 * Constructs a parameterized instance. 884 * 885 * @param taskId of the task to cancel 886 */ 887 public CancelTask(String taskId) { 888 super(taskId); 889 } 890 891 @Override 892 public MenuResult<TaskEntry> invoke(ManageTasks app) throws ClientException 893 { 894 try { 895 TaskEntry entry = app.getTaskClient().getTaskEntry(taskId); 896 if (!entry.isCancelable()) { 897 app.errPrintln(ERR_TASKINFO_TASK_NOT_CANCELABLE_TASK.get(taskId)); 898 return MenuResult.again(); 899 } 900 901 app.getTaskClient().cancelTask(taskId); 902 app.println(INFO_TASKINFO_CMD_CANCEL_SUCCESS.get(taskId)); 903 return MenuResult.success(entry); 904 } catch (Exception e) { 905 app.errPrintln(ERR_TASKINFO_CANCELING_TASK.get(taskId, e.getMessage())); 906 return MenuResult.again(); 907 } 908 } 909 } 910}