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