protected void doFetchLiveLog() {
    JobOutput currentOutput = this.model.getCurrentOutput();
    final String jobId = currentOutput.getJobId();
    if (!(currentOutput.isLive() && currentOutput.isLiveEnabled())) {
      LogModel.getInstance()
          .logMessage("stop fetching live logs and disable live for job " + jobId);
      return;
    }

    SchedulerServiceAsync scheduler = Scheduler.getSchedulerService();
    scheduler.getLiveLogJob(
        LoginModel.getInstance().getSessionId(),
        jobId,
        new AsyncCallback<String>() {
          public void onSuccess(String result) {
            if (result.length() > 0) {
              LogModel.getInstance()
                  .logMessage(
                      "Fetched livelog chunk for job "
                          + jobId
                          + " ("
                          + result.length()
                          + " chars)");
              model.appendLiveOutput(jobId, result);
            }
          }

          public void onFailure(Throwable caught) {
            String msg = JSONUtils.getJsonErrorMessage(caught);
            LogModel.getInstance()
                .logImportantMessage("Failed to fetch live log for job " + jobId + ": " + msg);
          }
        });
  }
  /**
   * Fetch the output for a single task, store the result (or error message) in the model
   *
   * @param jobId id of the job containing this task
   * @param task task for which the output should be fetched
   * @param logMode one of {@link SchedulerServiceAsync#LOG_ALL}, {@link
   *     SchedulerServiceAsync#LOG_STDERR}, {@link SchedulerServiceAsync#LOG_STDOUT}
   */
  public void fetchTaskOutput(final String jobId, final Task task, final OutputMode logMode) {
    SchedulerServiceAsync scheduler = Scheduler.getSchedulerService();
    Request req =
        scheduler.getTaskOutput(
            LoginModel.getInstance().getSessionId(),
            "" + jobId,
            task.getName(),
            logMode,
            new AsyncCallback<String>() {
              public void onFailure(Throwable caught) {
                String msg = JSONUtils.getJsonErrorMessage(caught);
                // might be an exception
                try {
                  JSONObject json = JSONUtils.parseJSON(caught.getMessage()).isObject();
                  if (json.containsKey("stackTrace")) {
                    msg = json.get("stackTrace").isString().stringValue();
                    msg = msg.replace("\t", "&nbsp;&nbsp;&nbsp;&nbsp;");
                    msg = msg.replace("\n", "<br>");
                  }
                } catch (Throwable t) {
                  // not json
                }
                model.setTaskOutput(
                    jobId,
                    task,
                    "[" + task.getName() + "] <span style='color:red;'>" + msg + "</span>");
                LogModel.getInstance()
                    .logMessage(
                        "Failed to get output for task "
                            + task.getName()
                            + " in job "
                            + jobId /* + ": " + msg */);

                taskOutputRequests.remove("" + task.getId());
              }

              public void onSuccess(String result) {
                model.setTaskOutput(jobId, task, result);
                LogModel.getInstance()
                    .logMessage(
                        "Successfully fetched output for task "
                            + task.getName()
                            + " in job "
                            + jobId);

                taskOutputRequests.remove("" + task.getId());
              }
            });
    this.taskOutputRequests.put("" + task.getId(), req);
  }