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