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