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 }